From bad96775b6670335c54a1e9fafe739b11873d0a0 Mon Sep 17 00:00:00 2001 From: Marc Mettke Date: Sat, 6 Apr 2019 10:09:14 +0200 Subject: [PATCH 1/4] Adding Example for httpd with ldap Example contains a fully working system using an ldap server for developers. --- examples/.gitignore | 1 + examples/httpd-ldap/README.md | 27 ++++ examples/httpd-ldap/docker-compose.yml | 71 ++++++++++ examples/httpd-ldap/httpd.conf | 136 +++++++++++++++++++ examples/shared/config-ldap/config.ini | 151 ++++++++++++++++++++++ examples/shared/config-ldap/keys-sync | 27 ++++ examples/shared/config-ldap/keys-sync.pub | 1 + examples/shared/cron/root | 1 + examples/shared/ssmtp.conf | 4 + 9 files changed, 419 insertions(+) create mode 100644 examples/.gitignore create mode 100644 examples/httpd-ldap/README.md create mode 100644 examples/httpd-ldap/docker-compose.yml create mode 100644 examples/httpd-ldap/httpd.conf create mode 100644 examples/shared/config-ldap/config.ini create mode 100644 examples/shared/config-ldap/keys-sync create mode 100644 examples/shared/config-ldap/keys-sync.pub create mode 100644 examples/shared/cron/root create mode 100644 examples/shared/ssmtp.conf diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..9f4c740 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +db/ \ No newline at end of file diff --git a/examples/httpd-ldap/README.md b/examples/httpd-ldap/README.md new file mode 100644 index 0000000..4e7fd2d --- /dev/null +++ b/examples/httpd-ldap/README.md @@ -0,0 +1,27 @@ +# Example: httpd + ldap + +This Example shows how to use ska with httpd and ldap using docker. + +## Prepare setup + +1. Start system using `docker-compose up -d` +1. Visit http://localhost +1. Login using one of the following credentials: + +|Username|Password|Type| +|---|---|---| +|rainbow|password|admin| +|proceme|password|user| + +If something goes wrong, check the log using: +``` +docker logs -f httpd-ldap_ska_1 +``` + +## Using ska + +1. Login using the admin account `rainbow`. +1. Connect to the docker container using: `docker exec -it httpd-ldap_ska-php_1 /bin/ash` +1. Execute `/ska/scripts/ldap_update.php`. This will add the `admin` group and the `keys-sync` user +1. Add the server `test.example.com` at http://localhost/servers#add +1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. diff --git a/examples/httpd-ldap/docker-compose.yml b/examples/httpd-ldap/docker-compose.yml new file mode 100644 index 0000000..4e708a4 --- /dev/null +++ b/examples/httpd-ldap/docker-compose.yml @@ -0,0 +1,71 @@ +version: '2.2' +services: + test: + image: alpine:3.8 + command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes yes/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && /usr/sbin/sshd -D" + restart: always + expose: + - "22" + depends_on: + - ska-php + volumes: + - ../shared/config-ldap/keys-sync.pub:/key:ro + networks: + net: + aliases: + - test.example.com + + mail: + image: mwader/postfix-relay + restart: always + environment: + - POSTFIX_myhostname=ska.example.de + - POSTFIX_mynetworks=0.0.0.0/0 + expose: + - "25" + networks: + - net + + ska-db: + image: mariadb + restart: always + environment: + - MYSQL_ROOT_PASSWORD=root-password + - MYSQL_DATABASE=ska-db + - MYSQL_USER=ska-user + - MYSQL_PASSWORD=password + volumes: + - ./db:/var/lib/mysql:rw + networks: + - net + + ska-php: + image: alpine:3.8 + command: /bin/ash -c "mkdir -p /var/log/keys /run/php/ && (id -u keys-sync 2> /dev/null || adduser --system --disabled-password keys-sync) && chown keys-sync:nogroup /ska/config/keys-sync && apk add php php-fpm ssmtp openssh php7-json php7-ldap php7-mbstring php7-mysqli php7-ssh2 php7-posix php7-pcntl && sed -i -e '/listen =/ s/= .*/= 0.0.0.0:9000/' /etc/php7/php-fpm.d/www.conf && crond && /ska/scripts/syncd.php --user keys-sync && php-fpm7 -F" + restart: always + depends_on: + - ska-db + - mail + volumes: + - ../../:/ska/:ro + - ../shared/config-ldap/:/ska/config/:rw + - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro + - ../shared/cron/:/var/spool/cron/crontabs/:rw + networks: + - net + + ska: + image: httpd:alpine + restart: always + ports: + - "80:80" + depends_on: + - ska-php + volumes: + - ../../:/ska/:ro + - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro + networks: + - net + +networks: + net: diff --git a/examples/httpd-ldap/httpd.conf b/examples/httpd-ldap/httpd.conf new file mode 100644 index 0000000..92fdfca --- /dev/null +++ b/examples/httpd-ldap/httpd.conf @@ -0,0 +1,136 @@ +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +LoadModule filter_module modules/mod_filter.so +LoadModule mime_module modules/mod_mime.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule env_module modules/mod_env.so +LoadModule headers_module modules/mod_headers.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule version_module modules/mod_version.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule status_module modules/mod_status.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so + +LoadModule authnz_ldap_module modules/mod_authnz_ldap.so +LoadModule ldap_module modules/mod_ldap.so +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_html_module modules/mod_proxy_html.so +LoadModule proxy_connect_module modules/mod_proxy_connect.so +LoadModule proxy_ftp_module modules/mod_proxy_ftp.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +LoadModule proxy_scgi_module modules/mod_proxy_scgi.so +LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so +LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so +LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so +LoadModule proxy_ajp_module modules/mod_proxy_ajp.so +LoadModule proxy_balancer_module modules/mod_proxy_balancer.so +LoadModule proxy_express_module modules/mod_proxy_express.so +LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so +LoadModule proxy_http2_module modules/mod_proxy_http2.so +LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +LoadModule watchdog_module modules/mod_watchdog.so + + + + User daemon + Group daemon + + + + DirectoryIndex index.html + + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + CustomLog /proc/self/fd/1 common + + + + ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/" + + + + RequestHeader unset Proxy early + + + + TypesConfig conf/mime.types + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + + + + Include conf/extra/proxy-html.conf + + + + SSLRandomSeed startup builtin + SSLRandomSeed connect builtin + + + + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://ska-php:9000/ska/public_html/$1 + SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + + + + + AllowOverride none + Require all denied + + + + AllowOverride None + Options None + Require all granted + + + + AuthType Basic + AuthName "SSH Key Authority" + AuthBasicProvider ldap + + AuthLDAPBindDN "uid=rainbow,ou=users,dc=test,dc=itmettke,dc=de" + AuthLDAPBindPassword "password" + AuthLDAPURL "ldap://ldap-test.itmettke.de:389/ou=users,dc=test,dc=itmettke,dc=de?uid?sub?(&(objectClass=person))" STARTTLS + + Require valid-user + AllowOverride none + DirectoryIndex init.php + FallbackResource /init.php + Order allow,deny + Allow from all + + + + Require all denied + + + +Listen 80 +ServerAdmin admin@example.com +ServerRoot "/usr/local/apache2" +DocumentRoot "/ska/public_html" + +ErrorLog /proc/self/fd/2 +LogLevel warn + +LDAPVerifyServerCert off +AllowEncodedSlashes NoDecode diff --git a/examples/shared/config-ldap/config.ini b/examples/shared/config-ldap/config.ini new file mode 100644 index 0000000..40080a3 --- /dev/null +++ b/examples/shared/config-ldap/config.ini @@ -0,0 +1,151 @@ +; SSH Key Authority config file +[web] +enabled = 1 +baseurl = https://ska.example.com +logo = /logo-header-opera.png +; footer may contain HTML. Literal & " < and > should be escaped as & +; " < $gt; +footer = 'Developed by Opera Software.' + +[general] +; Use timeout --version to find out the current version +; used on e.g. debian +; timeout_util = GNU coreutils +; used on e.g. alpine +timeout_util = BusyBox + +[security] +; It is important that SKA is able to verify that it has connected to the +; server that it expected to connect to (otherwise it could be tricked into +; syncing the wrong keys to a server). The simplest way to accomplish this is +; through SSH host key verification. Setting either of the 2 options below to +; '0' can weaken the protection that SSH host key verification provides. + +; Determine who can reset a server's SSH host key in SKA: +; 0: Allow server admins to reset the SSH host key for servers that they +; administer +; 1: Full SKA admin access is required to reset a server's host key +host_key_reset_restriction = 1 + +; Determine what happens if multiple servers have the same SSH host key: +; 0: Allow sync to proceed +; 1: Abort sync of affected servers and report an error +; It is not recommended to leave this set to '0' indefinitely +host_key_collision_protection = 1 + + +; Hostname verification is a supplement to SSH host key verification for +; making sure that the sync process has connected to the server that it +; expected to. + +; Determine how hostname verification is performed: +; 0: Do not perform hostname verification +; 1: Compare with the result of `hostname -f` +; 2: Compare with /var/local/keys-sync/.hostnames, fall back to `hostname -f` +; if the file does not exist +; 3: Compare with /var/local/keys-sync/.hostnames, abort sync if the file +; does not exist +; The last option provides the most solid verification, as a server will only +; be synced to if it has been explicitly allowed on the server itself. +hostname_verification = 0 + +[defaults] +; This setting will cause new servers to always have a managed account called +; "root" and for that account to be automatically added into the +; "root-accounts" group: +; +; account_groups[root] = "root-accounts" +; +; Any number of these can be specified +account_groups[root] = "accounts-root" + +[email] +enabled = 1 +; The mail address that outgoing mails will be sent from +from_address = ska@example.com +from_name = "SSH Key Authority system" +; Where to mail security notifications to +report_address = reports@example.com +report_name = "SSH Key Authority reports" +; Where users should contact for help +admin_address = admin@example.com +admin_name = "SSH Key Authority administrators" +; You can use the reroute directive to redirect all outgoing mail to a single +; mail address - typically for temporary testing purposes +;reroute = test@example.com + +[database] +; Connection details to the MySQL database +hostname = ska-db +port = 3306 +username = ska-user +password = password +database = ska-db + +[ldap] +; Address to connect to LDAP server +host = ldap://ldap-test.itmettke.de:389 +; Use StartTLS for connection security (recommended if using ldap:// instead +; of ldaps:// above) +starttls = 1 +; LDAP subtree containing USER entries +dn_user = "ou=users,dc=test,dc=itmettke,dc=de" +; LDAP subtree containing GROUP entries +dn_group = "ou=groups,dc=test,dc=itmettke,dc=de" +; Set to 1 if the LDAP library should process referrals. In most cases this +; is not needed, and for AD servers it can cause errors when querying the +; whole tree. +follow_referrals = 0 + +; Leave bind_dn empty if binding is not required +bind_dn = "uid=rainbow,ou=users,dc=test,dc=itmettke,dc=de" +bind_password = "password" + +; User attributes +user_id = uid +user_name = cn +user_email = mail +;user_superior = superioremployee + +; If inactive users exist in your LDAP directory, filter with the following +; settings: +; Field to filter on: +;user_active = organizationalstatus +; Use *one* of user_active_true or user_active_false +; user_active_true means user is active if the user_active field equals its +; value +;user_active_true = 'current' +; user_active_false means user is active if the user_active field does not +; equal its value +;user_active_false = 'former' + +; Group membership attributes. Examples below are for typical setups: +; +; POSIX groups +; group_member = memberUid +; group_member_value = uid +; +; Group-of-names groups +; group_member = member +; group_member_value = dn +; +; Attribute of group where members are stored +group_member = member +; User attribute to compare with +group_member_value = dn + +; Members of admin_group are given full admin access to SSH Key Authority web +; interface +admin_group_cn = admin + +[inventory] +; SSH Key Authority will read the contents of the file /etc/uuid (if it +; exists) when syncing with a server. If a value is found, it can be used as a +; link to an inventory system. +; %s in the url directive will be replaced with the value found in /etc/uuid +;url = "https://inventory.example.com/device/%s" + +[gpg] +; SSH Key Authority can GPG sign outgoing emails sent from the +; email.from_address. To do this it needs to know an appropriate key ID to use +;key_id = 0123456789ABCDEF0123456789ABCDEF01234567 diff --git a/examples/shared/config-ldap/keys-sync b/examples/shared/config-ldap/keys-sync new file mode 100644 index 0000000..5ec63cc --- /dev/null +++ b/examples/shared/config-ldap/keys-sync @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqatsYGwteC4X+aIsNThluXZlh9SbdQTB3Zy8EoRcgKSEQdvw +sn56e3SLCTtLVGYCD7l5bTvjuJIG0VGKGG8ZYiESHQRBqm6t0zWEJMiOC0mUt3xW +4YJcVFhsk54WhVMWW7Irjx+wk9VbX4OellU0FZSjAs+hSernAOnVoSNXnSaC5hoq +98QWdlUwI+pOkKfyII4YYcSaPByNA/dx4Hy4cVqIaVIvDf4SiYHnqbCCZbSZcrQD +W/pLnq6s4e3LvYNaB2BWLlbad8YuJRBRSp9C3thV1EmYIDQdlk6sg0fkFGYi+jWF +WKatA0u7Krt5Dh3BeeNgFqegfsb3E1P8+gUp6QIDAQABAoIBADdzCIYylGGUXs18 +jIoQFl6YLPJJL0z88waj9GrwyvJX0clcQbtzzj+OhOnNcP7yH3ZYHTDvw6pMPuIl +jcYSeO8y86J8A6HZbgF6mecTjChwMaQNhK9KVTZTd5h+r8l+r+3juoyZxLFrpuL+ +NtPWoKD05JlEled8V2ZbBTAWRsnahepwwIdVK9kroK/Z/O91YXHxsKkW8tr0BPAR +mJNFOZnMxKymyXZ05J9pEq+q054nDHdpOcKF6z1dNuVVpPvtPlJ+BfNZqLcQ87/3 +4AiX7hFEyiRQw6v5JXUXH1IkNuD3BpjFMAr5/g1damsWXw+rkeYk5uS44Em46LeU +hk2J8ykCgYEA1B9eZEI/xpejcXDLTR7NihF2kk3YmiHjnqvVRPV97/JlMw1kmulb +ga1Y4y7YLv0TT2lWTZ9U5d+aIGTLqDd2fFvUYwAojeibf8TIPxWj47IK1sG7K7/4 +fwtxzuYJ6TmehwKFLMqxW/RN8bxmXZOQyc0vdbWdc30filRpdJfkyMMCgYEAzMQG +NIc3WObmaNzT1qlC7svba0VWpE7Tve9yUlfQUzqiEPMWKxCxoxiSdjxKh3ZeFaGM +vb+EvvHryZVwO6Ym8YO3c7Or96/38dlpzR2USPPsE/z7q4Ai7p8s2OnpUdTN+Sbz +rUvWFZD+aTTzYaPHiknGGE4oTt90ksXhsHKR9+MCgYBZchzIZBMmd7zI0go4K9v5 +82EvUjoazF2CA+07VdVT/79ipqwckngul10a9x7buYWR/9YPhzcyc3Y3YKlr9m81 +Azfswx1WsZYYa28RAtwH4ISniCuXyCxNzhKgbuoQ+WX6gjqL57CgGMVhQNNddCMi +pS31Ea8iCsno9608J+ymJwKBgAupDgFnwcsaOvy1top+0QTwLuqI5EovAvfJ7uSp +g39500jLzvNc3ADBoXWCMWxNXUY2EOGtSk3lUNwF2oJLD+So74VzMdPE/YWUL+Yy +TQNqgIMS4PH/Nf4IqnEfwN2cFK6ffTjdHK/Vtecf0Mw6m15QnSs9KCZ1qxnAkv2N +SKqpAoGBAKb7H7PEoBlC0SfrdQIoqKcHX62IEVSS/bzeKRgOF8dNGeJkMLJeOzgK +uAdi8XYReKp6xkMz2KArfR55nkqGmqPHa9czxLcRh0afdccjK4kEgiCA3sGz80Hp +2r7EBnR6AK5aBTNDnpv2TQdOCTed5QBwHUWUWlD36xDayW/+cehH +-----END RSA PRIVATE KEY----- diff --git a/examples/shared/config-ldap/keys-sync.pub b/examples/shared/config-ldap/keys-sync.pub new file mode 100644 index 0000000..dce6e2a --- /dev/null +++ b/examples/shared/config-ldap/keys-sync.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@ska.example.com diff --git a/examples/shared/cron/root b/examples/shared/cron/root new file mode 100644 index 0000000..7ca495f --- /dev/null +++ b/examples/shared/cron/root @@ -0,0 +1 @@ +0 1 * * * /ska/scripts/ldap_update.php diff --git a/examples/shared/ssmtp.conf b/examples/shared/ssmtp.conf new file mode 100644 index 0000000..7e1ec6b --- /dev/null +++ b/examples/shared/ssmtp.conf @@ -0,0 +1,4 @@ +mailhub=mail +rewriteDomain=example.com +hostname="ska" +FromLineOverride=YES From caa65e4fc3343b30da8eaad3727f28faf6aa052d Mon Sep 17 00:00:00 2001 From: Marc Mettke Date: Sat, 6 Apr 2019 11:35:56 +0200 Subject: [PATCH 2/4] Adding docker container --- docker/Dockerfile | 39 ++++++++++++++++++++++++++ docker/cron | 2 ++ docker/entrypoint.sh | 35 +++++++++++++++++++++++ docker/healthcheck.sh | 7 +++++ examples/.gitignore | 3 +- examples/httpd-ldap/docker-compose.yml | 9 +++--- examples/shared/cron/root | 1 - 7 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/cron create mode 100644 docker/entrypoint.sh create mode 100644 docker/healthcheck.sh delete mode 100644 examples/shared/cron/root diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..24b9194 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,39 @@ +FROM alpine:3.8 +LABEL maintainer="Marc Mettke " + +ENV SYSTEM https://github.com/operasoftware/ssh-key-authority.git +ADD entrypoint.sh /entrypoint.sh +ADD healthcheck.sh /healthcheck.sh +ADD cron /var/spool/cron/crontabs/root + +RUN mkdir -p /var/log/keys/ /run/php/ /ska/ && \ + adduser --system --disabled-password keys-sync && \ + apk add openssh \ + php \ + php-fpm \ + php7-json \ + php7-ldap \ + php7-mbstring \ + php7-mysqli \ + php7-pcntl \ + php7-posix \ + php7-ssh2 \ + rsync \ + ssmtp \ + sudo && \ + sed -i -e '/listen =/ s/= .*/= 0.0.0.0:9000/' /etc/php7/php-fpm.d/www.conf && \ + sed -i -e '/;pid =/ s/.*/pid = \/var\/run\/php-fpm.pid/' /etc/php7/php-fpm.conf && \ + echo "" >> /etc/php7/php-fpm.conf && \ + chmod +x /entrypoint.sh /healthcheck.sh && \ + ln -sf /dev/stderr /var/log/php7/error.log +RUN apk add git && \ + git clone ${SYSTEM} /ska && \ + apk del git && \ + chown -R keys-sync:nogroup /ska/config + +EXPOSE 9000 +VOLUME /ska/config +VOLUME /public_html + +ENTRYPOINT "/entrypoint.sh" +HEALTHCHECK CMD /healcheck.sh diff --git a/docker/cron b/docker/cron new file mode 100644 index 0000000..6d10c59 --- /dev/null +++ b/docker/cron @@ -0,0 +1,2 @@ +0 1 * * * /ska/scripts/ldap_update.php +*/1 * * * * /bin/ash -c "PID=$(cat /var/run/keys-sync.pid) && [ -n ${PID} -a -d /proc/${PID} ] || /ska/scripts/syncd.php --user keys-sync" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..728eb7c --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env ash +if [ `whoami` == 'keys-sync' ]; then + if [ ! -r /ska/config/config.ini ]; then + echo "config.ini not found or incorrect permissions." + echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with at least 400" + exit 1 + fi + if [ ! -r /ska/config/keys-sync ]; then + echo "private key not found or incorrect permissions." + echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with 400" + exit 1 + fi + if [ ! -r /ska/config/keys-sync.pub ]; then + echo "public key not found or incorrect permissions." + echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with at least 400" + exit 1 + fi + if ! grep "^timeout_util = BusyBox$" /ska/config/config.ini > /dev/null; then + echo "timeout_util must be set to BusyBox." + echo "Change it to: timeout_util = BusyBox" + exit 1 + fi +elif [ $(id -u) = 0 ]; then + if ! sudo -u keys-sync /entrypoint.sh; then + exit 1 + fi + rsync -a --delete /ska/public_html/ /public_html/ + /usr/sbin/crond + echo "Waiting for database..." + sleep 5 + /ska/scripts/syncd.php --user keys-sync + /usr/sbin/php-fpm7 -F +else + echo "Must be executed with root" +fi diff --git a/docker/healthcheck.sh b/docker/healthcheck.sh new file mode 100644 index 0000000..8b38832 --- /dev/null +++ b/docker/healthcheck.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env ash +for PID_FILE in /var/run/crond.pid /var/run/keys-sync.pid /var/run/php-fpm.pid; do + PID=$(cat ${PID_FILE}) + if ! [ -n "${PID}" -a -d "/proc/${PID}" ]; then + exit 1 + fi +done diff --git a/examples/.gitignore b/examples/.gitignore index 9f4c740..21a7194 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1 +1,2 @@ -db/ \ No newline at end of file +db/ +public_html/ \ No newline at end of file diff --git a/examples/httpd-ldap/docker-compose.yml b/examples/httpd-ldap/docker-compose.yml index 4e708a4..8ee9f42 100644 --- a/examples/httpd-ldap/docker-compose.yml +++ b/examples/httpd-ldap/docker-compose.yml @@ -40,17 +40,16 @@ services: - net ska-php: - image: alpine:3.8 - command: /bin/ash -c "mkdir -p /var/log/keys /run/php/ && (id -u keys-sync 2> /dev/null || adduser --system --disabled-password keys-sync) && chown keys-sync:nogroup /ska/config/keys-sync && apk add php php-fpm ssmtp openssh php7-json php7-ldap php7-mbstring php7-mysqli php7-ssh2 php7-posix php7-pcntl && sed -i -e '/listen =/ s/= .*/= 0.0.0.0:9000/' /etc/php7/php-fpm.d/www.conf && crond && /ska/scripts/syncd.php --user keys-sync && php-fpm7 -F" + build: + context: ../../docker restart: always depends_on: - ska-db - mail volumes: - - ../../:/ska/:ro - ../shared/config-ldap/:/ska/config/:rw - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro - - ../shared/cron/:/var/spool/cron/crontabs/:rw + - ./public_html:/public_html:rw networks: - net @@ -62,7 +61,7 @@ services: depends_on: - ska-php volumes: - - ../../:/ska/:ro + - ./public_html:/ska/public_html:ro - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro networks: - net diff --git a/examples/shared/cron/root b/examples/shared/cron/root deleted file mode 100644 index 7ca495f..0000000 --- a/examples/shared/cron/root +++ /dev/null @@ -1 +0,0 @@ -0 1 * * * /ska/scripts/ldap_update.php From 34aa934afcfa67fdf24c76b3b19da9486d44fe26 Mon Sep 17 00:00:00 2001 From: Marc Mettke Date: Sun, 7 Apr 2019 07:12:49 +0200 Subject: [PATCH 3/4] Adding local user managment * keys-sync user becomes a local and not an ldap user * local users are allowed to access site without 403 * LDAP can be dis/enabled in the configuration * Added section to delete local user * Added section to add local users * Added graceful handling of deleted users in logs, nodes, ... There are tables containing NOT NULL fields for actors like the log table, which contains who did something. When we remove a user, we don't want to remove that log as the entity it is for is still alive. We instead want to remove the actor (or set it to null) so that the datbase stays consitent. The migrations in this patch recreate every table which has an actor to allow NULL fields. It also corrects a few constraints to set those values to NULL on delete. --- config/config-sample.ini | 1 + core.php | 10 ++- migrations/004.php | 159 +++++++++++++++++++++++++++++++++++ model/migrationdirectory.php | 2 +- model/user.php | 17 ++++ model/userdirectory.php | 41 ++++++--- requesthandler.php | 21 +++-- scripts/ldap_update.php | 22 +++-- scripts/sync.php | 1 + templates/functions.php | 4 + templates/group.php | 2 +- templates/server.php | 2 +- templates/user.php | 11 +++ templates/users.php | 74 ++++++++++++---- views/user.php | 9 +- views/users.php | 40 ++++++++- 16 files changed, 360 insertions(+), 56 deletions(-) create mode 100644 migrations/004.php diff --git a/config/config-sample.ini b/config/config-sample.ini index 030e70e..0b92cb7 100644 --- a/config/config-sample.ini +++ b/config/config-sample.ini @@ -83,6 +83,7 @@ password = password database = ska-db [ldap] +enabled = 0 ; Address to connect to LDAP server host = ldaps://ldap.example.com:636 ; Use StartTLS for connection security (recommended if using ldap:// instead diff --git a/core.php b/core.php index 618c15b..45acbb5 100644 --- a/core.php +++ b/core.php @@ -35,10 +35,12 @@ require('ldap.php'); require('email.php'); -$ldap_options = array(); -$ldap_options[LDAP_OPT_PROTOCOL_VERSION] = 3; -$ldap_options[LDAP_OPT_REFERRALS] = !empty($config['ldap']['follow_referrals']); -$ldap = new LDAP($config['ldap']['host'], $config['ldap']['starttls'], $config['ldap']['bind_dn'], $config['ldap']['bind_password'], $ldap_options); +if ($config['ldap']['enabled'] == 1) { + $ldap_options = array(); + $ldap_options[LDAP_OPT_PROTOCOL_VERSION] = 3; + $ldap_options[LDAP_OPT_REFERRALS] = !empty($config['ldap']['follow_referrals']); + $ldap = new LDAP($config['ldap']['host'], $config['ldap']['starttls'], $config['ldap']['bind_dn'], $config['ldap']['bind_password'], $ldap_options); +} setup_database(); $relative_frontend_base_url = (string)parse_url($config['web']['baseurl'], PHP_URL_PATH); diff --git a/migrations/004.php b/migrations/004.php new file mode 100644 index 0000000..9f088b7 --- /dev/null +++ b/migrations/004.php @@ -0,0 +1,159 @@ +store_result()) { + $res->free(); + } + } while ($database->more_results() && $database->next_result()); +} + +$this->database->autocommit(FALSE); + +$result = $this->database->query(" + SELECT uid FROM user WHERE uid = 'keys-sync' +"); +if ($result) { + if($result->num_rows === 0) { + $result->close(); + $result = $this->database->multi_query(" + INSERT INTO entity SET type = 'user'; + INSERT INTO user SET entity_id = ( + SELECT LAST_INSERT_ID() + ), uid = 'keys-sync', name = 'Synchronization script', email = '', auth_realm = 'local', admin = 1; + "); + free_results($this->database); + } else { + $result->close(); + $this->database->query(" + UPDATE user SET auth_realm = 'local', active = 1 WHERE uid = 'keys-sync'; + "); + } +} + + +$this->database->multi_query(" +CREATE TABLE `entity_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `entity_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_entity_event_entity_id` (`entity_id`), + KEY `FK_entity_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT entity_event_2 SELECT * FROM entity_event; + +DROP TABLE entity_event; +RENAME TABLE entity_event_2 TO entity_event; + +ALTER TABLE `entity_event` + ADD CONSTRAINT `FK_entity_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_entity_event_entity_id` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `group_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `group` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_group_event_group` (`group`), + KEY `FK_group_event_entity` (`entity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; + +INSERT group_event_2 SELECT * FROM group_event; + +DROP TABLE group_event; +RENAME TABLE group_event_2 TO group_event; + +ALTER TABLE `group_event` + ADD CONSTRAINT `FK_group_event_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_group_event_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `group_member_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `group` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned NOT NULL, + `add_date` datetime NOT NULL, + `added_by` int(10) unsigned, + PRIMARY KEY (`id`), + UNIQUE KEY `group_entity_id` (`group`, `entity_id`), + KEY `FK_group_member_entity` (`entity_id`), + KEY `FK_group_member_entity_2` (`added_by`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; + +INSERT group_member_2 SELECT * FROM group_member; + +DROP TABLE group_member; +RENAME TABLE group_member_2 TO group_member; + +ALTER TABLE `group_member` + ADD CONSTRAINT `FK_group_member_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `FK_group_member_entity_2` FOREIGN KEY (`added_by`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_group_member_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `server_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_log_server` (`server_id`), + KEY `FK_server_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT server_event_2 SELECT * FROM server_event; + +DROP TABLE server_event; +RENAME TABLE server_event_2 TO server_event; + +ALTER TABLE `server_event` + ADD CONSTRAINT `FK_server_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_log_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `server_note_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned, + `date` datetime NOT NULL, + `note` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_note_server` (`server_id`), + KEY `FK_server_note_user` (`entity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT server_note_2 SELECT * FROM server_note; + +DROP TABLE server_note; +RENAME TABLE server_note_2 TO server_note; + +ALTER TABLE `server_note` + ADD CONSTRAINT `FK_server_note_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_note_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE +"); +free_results($this->database); + +$this->database->commit(); + +$this->database->autocommit(TRUE); diff --git a/model/migrationdirectory.php b/model/migrationdirectory.php index fabbc84..dd6a60c 100644 --- a/model/migrationdirectory.php +++ b/model/migrationdirectory.php @@ -22,7 +22,7 @@ class MigrationDirectory extends DBDirectory { /** * Increment this constant to activate a new migration from the migrations directory */ - const LAST_MIGRATION = 3; + const LAST_MIGRATION = 4; public function __construct() { parent::__construct(); diff --git a/model/user.php b/model/user.php index 6b7774e..deba94c 100644 --- a/model/user.php +++ b/model/user.php @@ -66,6 +66,23 @@ public function update() { } } + /** + * Delete the given user. + */ + public function delete() { + if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before it can be removed'); + $stmt = $this->database->prepare("DELETE FROM entity WHERE id = ?"); + $stmt->bind_param('d', $this->entity_id); + $stmt->execute(); + $stmt->close(); + $stmt = $this->database->prepare("DELETE FROM user WHERE entity_id = ?"); + $stmt->bind_param('d', $this->entity_id); + $stmt->execute(); + $stmt->close(); + + $this->sync_remote_access(); + } + /** * Magic getter method - if superior field requested, return User object of user's superior * @param string $field to retrieve diff --git a/model/userdirectory.php b/model/userdirectory.php index 8227b6b..5ff6b82 100644 --- a/model/userdirectory.php +++ b/model/userdirectory.php @@ -45,13 +45,22 @@ public function add_user(User $user) { $user_active = $user->active; $user_admin = $user->admin; $user_email = $user->email; - $stmt = $this->database->prepare("INSERT INTO entity SET type = 'user'"); - $stmt->execute(); - $user->entity_id = $stmt->insert_id; - $stmt = $this->database->prepare("INSERT INTO user SET entity_id = ?, uid = ?, name = ?, email = ?, active = ?, admin = ?"); - $stmt->bind_param('dsssdd', $user->entity_id, $user_id, $user_name, $user_email, $user_active, $user_admin); - $stmt->execute(); - $stmt->close(); + try { + $stmt = $this->database->prepare("INSERT INTO entity SET type = 'user'"); + $stmt->execute(); + $user->entity_id = $stmt->insert_id; + $stmt = $this->database->prepare("INSERT INTO user SET entity_id = ?, uid = ?, name = ?, email = ?, active = ?, admin = ?, auth_realm = ?"); + $stmt->bind_param('dsssdds', $user->entity_id, $user_id, $user_name, $user_email, $user_active, $user_admin, $user->auth_realm); + $stmt->execute(); + $stmt->close(); + } catch(mysqli_sql_exception $e) { + if($e->getCode() == 1062) { + // Duplicate entry + throw new UserAlreadyExistsException("User {$user->uid} already exists"); + } else { + throw $e; + } + } } /** @@ -82,6 +91,8 @@ public function get_user_by_id($id) { * @throws UserNotFoundException if no user with that uid exists */ public function get_user_by_uid($uid) { + global $config; + $ldap_enabled = $config['ldap']['enabled']; if(isset($this->cache_uid[$uid])) { return $this->cache_uid[$uid]; } @@ -93,11 +104,16 @@ public function get_user_by_uid($uid) { $user = new User($row['entity_id'], $row); $this->cache_uid[$uid] = $user; } else { - $user = new User; - $user->uid = $uid; - $this->cache_uid[$uid] = $user; - $user->get_details_from_ldap(); - $this->add_user($user); + if ($ldap_enabled == 1) { + $user = new User; + $user->uid = $uid; + $this->cache_uid[$uid] = $user; + $user->auth_realm = 'LDAP'; + $user->get_details_from_ldap(); + $this->add_user($user); + } else { + throw new UserNotFoundException('User does not exist.'); + } } $stmt->close(); return $user; @@ -148,3 +164,4 @@ public function list_users($include = array(), $filter = array()) { } class UserNotFoundException extends Exception {} +class UserAlreadyExistsException extends Exception {} diff --git a/requesthandler.php b/requesthandler.php index 5b921bf..cc60cd0 100644 --- a/requesthandler.php +++ b/requesthandler.php @@ -20,12 +20,6 @@ ob_start(); set_exception_handler('exception_handler'); -if(isset($_SERVER['PHP_AUTH_USER'])) { - $active_user = $user_dir->get_user_by_uid($_SERVER['PHP_AUTH_USER']); -} else { - throw new Exception("Not logged in."); -} - // Work out where we are on the server $base_path = dirname(__FILE__); $base_url = dirname($_SERVER['SCRIPT_NAME']); @@ -33,6 +27,17 @@ $relative_request_url = preg_replace('/^'.preg_quote($base_url, '/').'/', '/', $request_url); $absolute_request_url = 'http'.(isset($_SERVER['HTTPS']) ? 's' : '').'://'.$_SERVER['HTTP_HOST'].$request_url; +if(isset($_SERVER['PHP_AUTH_USER'])) { + try { + $active_user = $user_dir->get_user_by_uid($_SERVER['PHP_AUTH_USER']); + } catch(UserNotFoundException $ex) { + require('views/error403.php'); + die; + } +} else { + throw new Exception("Not logged in."); +} + if(empty($config['web']['enabled'])) { require('views/error503.php'); die; @@ -40,6 +45,7 @@ if(!$active_user->active) { require('views/error403.php'); + die; } if(!empty($_POST)) { @@ -62,10 +68,11 @@ if(isset($router->view)) { $view = path_join($base_path, 'views', $router->view.'.php'); if(file_exists($view)) { - if($active_user->auth_realm == 'LDAP' || $router->public) { + if($active_user->auth_realm == 'LDAP' || $active_user->auth_realm == 'local' || $router->public) { require($view); } else { require('views/error403.php'); + die; } } else { throw new Exception("View file $view missing."); diff --git a/scripts/ldap_update.php b/scripts/ldap_update.php index 6f8668f..076fca6 100755 --- a/scripts/ldap_update.php +++ b/scripts/ldap_update.php @@ -29,22 +29,28 @@ $active_user->uid = 'keys-sync'; $active_user->name = 'Synchronization script'; $active_user->email = ''; + $active_user->auth_realm = 'local'; $active_user->active = 1; $active_user->admin = 1; $active_user->developer = 0; $user_dir->add_user($active_user); } -try { - $sysgrp = $group_dir->get_group_by_name($config['ldap']['admin_group_cn']); -} catch(GroupNotFoundException $e) { - $sysgrp = new Group; - $sysgrp->name = $config['ldap']['admin_group_cn']; - $sysgrp->system = 1; - $group_dir->add_group($sysgrp); +$ldap_enabled = $config['ldap']['enabled']; + +if($ldap_enabled == 1) { + try { + $sysgrp = $group_dir->get_group_by_name($config['ldap']['admin_group_cn']); + } catch(GroupNotFoundException $e) { + $sysgrp = new Group; + $sysgrp->name = $config['ldap']['admin_group_cn']; + $sysgrp->system = 1; + $group_dir->add_group($sysgrp); + } } + foreach($users as $user) { - if($user->auth_realm == 'LDAP') { + if($user->auth_realm == 'LDAP' && $ldap_enabled == 1) { $active = $user->active; try { $user->get_details_from_ldap(); diff --git a/scripts/sync.php b/scripts/sync.php index 208818b..1390e32 100755 --- a/scripts/sync.php +++ b/scripts/sync.php @@ -69,6 +69,7 @@ $active_user->uid = 'keys-sync'; $active_user->name = 'Synchronization script'; $active_user->email = ''; + $active_user->auth_realm = 'local'; $active_user->active = 1; $active_user->admin = 1; $active_user->developer = 0; diff --git a/templates/functions.php b/templates/functions.php index aaf4184..758d395 100644 --- a/templates/functions.php +++ b/templates/functions.php @@ -85,7 +85,11 @@ function show_event($event) { group->name) ?> + actor->uid)) { ?> + removed + actor->uid) ?> + date) ?> diff --git a/templates/group.php b/templates/group.php index cd0a6d9..569c116 100644 --- a/templates/group.php +++ b/templates/group.php @@ -82,7 +82,7 @@ break; } ?> - Added on add_date) ?> by added_by->uid) ?> + Added on add_date) ?> by added_by->uid)) { ?>removedadded_by->uid) ?> get('group')->system) { ?> diff --git a/templates/server.php b/templates/server.php index 2c493d7..7e11bb9 100644 --- a/templates/server.php +++ b/templates/server.php @@ -451,7 +451,7 @@
get('output_formatter')->comment_format($note->note), ESC_NONE)?>
diff --git a/templates/user.php b/templates/user.php index a4f8d7e..c7f18c7 100644 --- a/templates/user.php +++ b/templates/user.php @@ -217,6 +217,17 @@ + get('user')->auth_realm == 'local' && $this->get('admin')) { ?> +

User managment

+
+ get('active_user')->get_csrf_field(), ESC_NONE) ?> +
+
+ +
+
+
+ get('user')->auth_realm == 'LDAP') { ?>
diff --git a/templates/users.php b/templates/users.php index 072303a..1d13ad0 100644 --- a/templates/users.php +++ b/templates/users.php @@ -16,21 +16,59 @@ ## ?>

Users

- - - - - - - - - - get('users') as $user) { ?> - active) out(' class="text-muted"', ESC_NONE) ?>> - - - - - - -
UsernameFull namePublic keys
uid)?>name)?>list_public_keys())))?>
+get('admin')) { ?> + + + + + +
+
+

User list

+

get('users')); out(number_format($total).' user'.($total == 1 ? '' : 's').' found')?>

+ + + + + + + + + + get('users') as $user) { ?> + active) out(' class="text-muted"', ESC_NONE) ?>> + + + + + + +
UsernameFull namePublic keys
uid)?>name)?>list_public_keys())))?>
+
+ + get('admin')) { ?> +
+

Add user

+
+ get('active_user')->get_csrf_field(), ESC_NONE) ?> +
+ + +
+
+ + +
+
+ + +
+ Administrator

+ +
+
+ +
diff --git a/views/user.php b/views/user.php index f871308..1f59f50 100644 --- a/views/user.php +++ b/views/user.php @@ -48,9 +48,16 @@ } } elseif(isset($_POST['edit_user']) && $active_user->admin) { $user->force_disable = $_POST['force_disable']; - $user->get_details_from_ldap(); + if($active_user->auth_realm == 'LDAP' ) { + $user->get_details_from_ldap(); + } $user->update(); redirect('#settings'); +} elseif(isset($_POST['delete_user']) && $active_user->admin) { + if($user->auth_realm == 'local' && $user->uid != 'keys-sync' ) { + $user->delete(); + } + redirect('/users'); } else { $content = new PageSection('user'); $content->set('user', $user); diff --git a/views/users.php b/views/users.php index 72ddf8b..c129b79 100644 --- a/views/users.php +++ b/views/users.php @@ -15,10 +15,44 @@ ## limitations under the License. ## -$content = new PageSection('users'); -$content->set('users', $user_dir->list_users()); -$content->set('admin', $active_user->admin); +if(isset($_POST['add_user']) && $active_user->admin) { + $uid = trim($_POST['uid']); + $name = trim($_POST['name']); + $email = trim($_POST['email']); + + $user = new User; + $user->uid = $uid; + $user->name = $name; + $user->email = $email; + + $user->active = 1; + if (isset($_POST['admin']) && $_POST['admin'] === 'admin') { + $user->admin = 1; + } else { + $user->admin = 0; + } + $user->auth_realm = 'local'; + try { + $user_dir->add_user($user); + $alert = new UserAlert; + $alert->content = 'User \''.hesc($user->uid).'\' successfully created.'; + $alert->escaping = ESC_NONE; + $active_user->add_alert($alert); + } catch(UserAlreadyExistsException $e) { + $alert = new UserAlert; + $alert->content = 'User \''.hesc($user->uid).'\' is already known by SSH Key Authority.'; + $alert->escaping = ESC_NONE; + $alert->class = 'danger'; + $active_user->add_alert($alert); + } + redirect('#add'); +} else { + $content = new PageSection('users'); + $content->set('users', $user_dir->list_users()); + $content->set('admin', $active_user->admin); +} + $page = new PageSection('base'); $page->set('title', 'Users'); $page->set('content', $content); From ff719dc56862ce04944dc1cdf16d23b954551322 Mon Sep 17 00:00:00 2001 From: Marc Mettke Date: Sun, 7 Apr 2019 09:14:59 +0200 Subject: [PATCH 4/4] Followup on the ldap example. * Provides examples for nginx and httpd using htpasswd --- examples/httpd-local/README.md | 34 +++++ examples/httpd-local/docker-compose.yml | 71 ++++++++++ examples/httpd-local/htpasswd.conf | 4 + examples/httpd-local/httpd.conf | 133 ++++++++++++++++++ examples/nginx-local/README.md | 34 +++++ examples/nginx-local/docker-compose.yml | 71 ++++++++++ examples/nginx-local/htpasswd.conf | 4 + examples/nginx-local/nginx.conf | 30 ++++ examples/shared/config-ldap/config.ini | 1 + examples/shared/config-local/config.ini | 152 +++++++++++++++++++++ examples/shared/config-local/keys-sync | 27 ++++ examples/shared/config-local/keys-sync.pub | 1 + 12 files changed, 562 insertions(+) create mode 100644 examples/httpd-local/README.md create mode 100644 examples/httpd-local/docker-compose.yml create mode 100644 examples/httpd-local/htpasswd.conf create mode 100644 examples/httpd-local/httpd.conf create mode 100644 examples/nginx-local/README.md create mode 100644 examples/nginx-local/docker-compose.yml create mode 100644 examples/nginx-local/htpasswd.conf create mode 100644 examples/nginx-local/nginx.conf create mode 100644 examples/shared/config-local/config.ini create mode 100644 examples/shared/config-local/keys-sync create mode 100644 examples/shared/config-local/keys-sync.pub diff --git a/examples/httpd-local/README.md b/examples/httpd-local/README.md new file mode 100644 index 0000000..862b7c8 --- /dev/null +++ b/examples/httpd-local/README.md @@ -0,0 +1,34 @@ +# Example: httpd + htpasswd + +This Example shows how to use ska with httpd and ldap using docker. + +## Prepare setup + +1. Start system using `docker-compose up -d` +1. Visit http://localhost +1. Login using one of the following credentials (Only keys-sync account exists at first): + +|Username|Password|Type| +|---|---|---| +|keys-sync|password|admin| +|rainbow|password|admin| +|proceme|password|user| + +If something goes wrong, check the log using: +``` +docker logs -f httpd-local_ska_1 +``` + +## Using ska + +_The `keys-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ + +1. Login using the admin account `keys-sync`. +1. Create user `rainbow` as admin and user `proceme` as user at http://localhost/users#add +1. Add the server `test.example.com` at http://localhost/servers#add +1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. + +## Add/Change passwords for users + +1. Either install `htpasswd` on your system or connect to the httpd container using `docker exec -it httpd-local_ska_1 /bin/ash`. +1. Run `htpasswd` on the htpasswd file. Inside the container it is `htpasswd /allowed_users ` diff --git a/examples/httpd-local/docker-compose.yml b/examples/httpd-local/docker-compose.yml new file mode 100644 index 0000000..8b4d8e9 --- /dev/null +++ b/examples/httpd-local/docker-compose.yml @@ -0,0 +1,71 @@ +version: '2.2' +services: + test: + image: alpine:3.8 + command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes yes/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && /usr/sbin/sshd -D" + restart: always + expose: + - "22" + depends_on: + - ska-php + volumes: + - ../shared/config-local/keys-sync.pub:/key:ro + networks: + net: + aliases: + - test.example.com + + mail: + image: mwader/postfix-relay + restart: always + environment: + - POSTFIX_myhostname=ska.example.de + - POSTFIX_mynetworks=0.0.0.0/0 + expose: + - "25" + networks: + - net + + ska-db: + image: mariadb + restart: always + environment: + - MYSQL_ROOT_PASSWORD=root-password + - MYSQL_DATABASE=ska-db + - MYSQL_USER=ska-user + - MYSQL_PASSWORD=password + volumes: + - ./db:/var/lib/mysql:rw + networks: + - net + + ska-php: + build: + context: ../../docker + restart: always + depends_on: + - ska-db + - mail + volumes: + - ../shared/config-local/:/ska/config/:rw + - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro + - ./public_html:/public_html:rw + networks: + - net + + ska: + image: httpd:alpine + restart: always + ports: + - "80:80" + depends_on: + - ska-php + volumes: + - ./public_html:/ska/public_html:ro + - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro + - ./htpasswd.conf:/allowed_users:rw + networks: + - net + +networks: + net: diff --git a/examples/httpd-local/htpasswd.conf b/examples/httpd-local/htpasswd.conf new file mode 100644 index 0000000..1e9bc10 --- /dev/null +++ b/examples/httpd-local/htpasswd.conf @@ -0,0 +1,4 @@ +keys-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 +rainbow:$apr1$uBkkgFwm$zrLgjA/5R8V1P0F/XHKUV0 +proceme:$apr1$SWQKwjuM$D0FWOsBAD4lTdcYrtbZ0j0 +speecif:$apr1$MCOzQE15$K4uameMTLTf3fJDSj/XfE. diff --git a/examples/httpd-local/httpd.conf b/examples/httpd-local/httpd.conf new file mode 100644 index 0000000..04c3f74 --- /dev/null +++ b/examples/httpd-local/httpd.conf @@ -0,0 +1,133 @@ +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +LoadModule filter_module modules/mod_filter.so +LoadModule mime_module modules/mod_mime.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule env_module modules/mod_env.so +LoadModule headers_module modules/mod_headers.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule version_module modules/mod_version.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule status_module modules/mod_status.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so + +LoadModule authnz_ldap_module modules/mod_authnz_ldap.so +LoadModule ldap_module modules/mod_ldap.so +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_html_module modules/mod_proxy_html.so +LoadModule proxy_connect_module modules/mod_proxy_connect.so +LoadModule proxy_ftp_module modules/mod_proxy_ftp.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +LoadModule proxy_scgi_module modules/mod_proxy_scgi.so +LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so +LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so +LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so +LoadModule proxy_ajp_module modules/mod_proxy_ajp.so +LoadModule proxy_balancer_module modules/mod_proxy_balancer.so +LoadModule proxy_express_module modules/mod_proxy_express.so +LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so +LoadModule proxy_http2_module modules/mod_proxy_http2.so +LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +LoadModule watchdog_module modules/mod_watchdog.so + + + + User daemon + Group daemon + + + + DirectoryIndex index.html + + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + CustomLog /proc/self/fd/1 common + + + + ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/" + + + + RequestHeader unset Proxy early + + + + TypesConfig conf/mime.types + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + + + + Include conf/extra/proxy-html.conf + + + + SSLRandomSeed startup builtin + SSLRandomSeed connect builtin + + + + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://ska-php:9000/ska/public_html/$1 + SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + + + + + AllowOverride none + Require all denied + + + + AllowOverride None + Options None + Require all granted + + + + AuthType Basic + AuthName "SSH Key Authority" + AuthBasicProvider file + AuthUserFile /allowed_users + + Require valid-user + AllowOverride none + DirectoryIndex init.php + FallbackResource /init.php + Order allow,deny + Allow from all + + + + Require all denied + + + +Listen 80 +ServerAdmin admin@example.com +ServerRoot "/usr/local/apache2" +DocumentRoot "/ska/public_html" + +ErrorLog /proc/self/fd/2 +LogLevel warn + +LDAPVerifyServerCert off +AllowEncodedSlashes NoDecode diff --git a/examples/nginx-local/README.md b/examples/nginx-local/README.md new file mode 100644 index 0000000..42f326c --- /dev/null +++ b/examples/nginx-local/README.md @@ -0,0 +1,34 @@ +# Example: nginx + htpasswd + +This Example shows how to use ska with nginx and ldap using docker. + +## Prepare setup + +1. Start system using `docker-compose up -d` +1. Visit http://localhost +1. Login using one of the following credentials (Only keys-sync account exists at first): + +|Username|Password|Type| +|---|---|---| +|keys-sync|password|admin| +|rainbow|password|admin| +|proceme|password|user| + +If something goes wrong, check the log using: +``` +docker logs -f nginx-local_ska_1 +``` + +## Using ska + +_The `keys-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ + +1. Login using the admin account `keys-sync`. +1. Create user `rainbow` as admin and user `proceme` as user at http://localhost/users#add +1. Add the server `test.example.com` at http://localhost/servers#add +1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. + +## Add/Change passwords for users + +1. Either install `htpasswd` on your system or connect to the nginx container using `docker exec -it nginx-local_ska_1 /bin/ash` and install it there with `apk add apache2-utils` +1. Run `htpasswd` on the htpasswd file. Inside the container it is `htpasswd /allowed_users ` diff --git a/examples/nginx-local/docker-compose.yml b/examples/nginx-local/docker-compose.yml new file mode 100644 index 0000000..9da791b --- /dev/null +++ b/examples/nginx-local/docker-compose.yml @@ -0,0 +1,71 @@ +version: '2.2' +services: + test: + image: alpine:3.8 + command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes yes/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && /usr/sbin/sshd -D" + restart: always + expose: + - "22" + depends_on: + - ska-php + volumes: + - ../shared/config-local/keys-sync.pub:/key:ro + networks: + net: + aliases: + - test.example.com + + mail: + image: mwader/postfix-relay + restart: always + environment: + - POSTFIX_myhostname=ska.example.de + - POSTFIX_mynetworks=0.0.0.0/0 + expose: + - "25" + networks: + - net + + ska-db: + image: mariadb + restart: always + environment: + - MYSQL_ROOT_PASSWORD=root-password + - MYSQL_DATABASE=ska-db + - MYSQL_USER=ska-user + - MYSQL_PASSWORD=password + volumes: + - ./db:/var/lib/mysql:rw + networks: + - net + + ska-php: + build: + context: ../../docker + restart: always + depends_on: + - ska-db + - mail + volumes: + - ../shared/config-local/:/ska/config/:rw + - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro + - ./public_html:/public_html:rw + networks: + - net + + ska: + image: nginx:alpine + restart: always + ports: + - "80:80" + depends_on: + - ska-php + volumes: + - ./public_html:/ska/public_html:ro + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./htpasswd.conf:/allowed_users:rw + networks: + - net + +networks: + net: diff --git a/examples/nginx-local/htpasswd.conf b/examples/nginx-local/htpasswd.conf new file mode 100644 index 0000000..1e9bc10 --- /dev/null +++ b/examples/nginx-local/htpasswd.conf @@ -0,0 +1,4 @@ +keys-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 +rainbow:$apr1$uBkkgFwm$zrLgjA/5R8V1P0F/XHKUV0 +proceme:$apr1$SWQKwjuM$D0FWOsBAD4lTdcYrtbZ0j0 +speecif:$apr1$MCOzQE15$K4uameMTLTf3fJDSj/XfE. diff --git a/examples/nginx-local/nginx.conf b/examples/nginx-local/nginx.conf new file mode 100644 index 0000000..b6f4af1 --- /dev/null +++ b/examples/nginx-local/nginx.conf @@ -0,0 +1,30 @@ + +server { + listen 80; + server_name ska.example.com; + + root /ska/public_html; + index init.php; + + auth_basic "SSH Key Authority"; + auth_basic_user_file /allowed_users; + + location / { + try_files $uri $uri/ @php; + } + + location @php { + rewrite ^/(.*)$ /init.php/$1 last; + } + + location /init.php { + # Mitigate https://httpoxy.org/ vulnerabilities + fastcgi_param HTTP_PROXY ""; + fastcgi_pass ska-php:9000; + + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTPS off; + } +} diff --git a/examples/shared/config-ldap/config.ini b/examples/shared/config-ldap/config.ini index 40080a3..9d4f567 100644 --- a/examples/shared/config-ldap/config.ini +++ b/examples/shared/config-ldap/config.ini @@ -83,6 +83,7 @@ password = password database = ska-db [ldap] +enabled = 1 ; Address to connect to LDAP server host = ldap://ldap-test.itmettke.de:389 ; Use StartTLS for connection security (recommended if using ldap:// instead diff --git a/examples/shared/config-local/config.ini b/examples/shared/config-local/config.ini new file mode 100644 index 0000000..c00840c --- /dev/null +++ b/examples/shared/config-local/config.ini @@ -0,0 +1,152 @@ +; SSH Key Authority config file +[web] +enabled = 1 +baseurl = https://ska.example.com +logo = /logo-header-opera.png +; footer may contain HTML. Literal & " < and > should be escaped as & +; " < $gt; +footer = 'Developed by Opera Software.' + +[general] +; Use timeout --version to find out the current version +; used on e.g. debian +; timeout_util = GNU coreutils +; used on e.g. alpine +timeout_util = BusyBox + +[security] +; It is important that SKA is able to verify that it has connected to the +; server that it expected to connect to (otherwise it could be tricked into +; syncing the wrong keys to a server). The simplest way to accomplish this is +; through SSH host key verification. Setting either of the 2 options below to +; '0' can weaken the protection that SSH host key verification provides. + +; Determine who can reset a server's SSH host key in SKA: +; 0: Allow server admins to reset the SSH host key for servers that they +; administer +; 1: Full SKA admin access is required to reset a server's host key +host_key_reset_restriction = 1 + +; Determine what happens if multiple servers have the same SSH host key: +; 0: Allow sync to proceed +; 1: Abort sync of affected servers and report an error +; It is not recommended to leave this set to '0' indefinitely +host_key_collision_protection = 1 + + +; Hostname verification is a supplement to SSH host key verification for +; making sure that the sync process has connected to the server that it +; expected to. + +; Determine how hostname verification is performed: +; 0: Do not perform hostname verification +; 1: Compare with the result of `hostname -f` +; 2: Compare with /var/local/keys-sync/.hostnames, fall back to `hostname -f` +; if the file does not exist +; 3: Compare with /var/local/keys-sync/.hostnames, abort sync if the file +; does not exist +; The last option provides the most solid verification, as a server will only +; be synced to if it has been explicitly allowed on the server itself. +hostname_verification = 0 + +[defaults] +; This setting will cause new servers to always have a managed account called +; "root" and for that account to be automatically added into the +; "root-accounts" group: +; +; account_groups[root] = "root-accounts" +; +; Any number of these can be specified +account_groups[root] = "accounts-root" + +[email] +enabled = 1 +; The mail address that outgoing mails will be sent from +from_address = ska@example.com +from_name = "SSH Key Authority system" +; Where to mail security notifications to +report_address = reports@example.com +report_name = "SSH Key Authority reports" +; Where users should contact for help +admin_address = admin@example.com +admin_name = "SSH Key Authority administrators" +; You can use the reroute directive to redirect all outgoing mail to a single +; mail address - typically for temporary testing purposes +;reroute = test@example.com + +[database] +; Connection details to the MySQL database +hostname = ska-db +port = 3306 +username = ska-user +password = password +database = ska-db + +[ldap] +enabled = 0 +; Address to connect to LDAP server +host = ldaps://ldap.example.com:636 +; Use StartTLS for connection security (recommended if using ldap:// instead +; of ldaps:// above) +starttls = 0 +; LDAP subtree containing USER entries +dn_user = "ou=users,dc=example,dc=com" +; LDAP subtree containing GROUP entries +dn_group = "ou=groups,dc=example,dc=com" +; Set to 1 if the LDAP library should process referrals. In most cases this +; is not needed, and for AD servers it can cause errors when querying the +; whole tree. +follow_referrals = 0 + +; Leave bind_dn empty if binding is not required +bind_dn = +bind_password = + +; User attributes +user_id = uid +user_name = cn +user_email = mail +;user_superior = superioremployee + +; If inactive users exist in your LDAP directory, filter with the following +; settings: +; Field to filter on: +;user_active = organizationalstatus +; Use *one* of user_active_true or user_active_false +; user_active_true means user is active if the user_active field equals its +; value +;user_active_true = 'current' +; user_active_false means user is active if the user_active field does not +; equal its value +;user_active_false = 'former' + +; Group membership attributes. Examples below are for typical setups: +; +; POSIX groups +; group_member = memberUid +; group_member_value = uid +; +; Group-of-names groups +; group_member = member +; group_member_value = dn +; +; Attribute of group where members are stored +group_member = memberUid +; User attribute to compare with +group_member_value = uid + +; Members of admin_group are given full admin access to SSH Key Authority web +; interface +admin_group_cn = ska-administrators + +[inventory] +; SSH Key Authority will read the contents of the file /etc/uuid (if it +; exists) when syncing with a server. If a value is found, it can be used as a +; link to an inventory system. +; %s in the url directive will be replaced with the value found in /etc/uuid +;url = "https://inventory.example.com/device/%s" + +[gpg] +; SSH Key Authority can GPG sign outgoing emails sent from the +; email.from_address. To do this it needs to know an appropriate key ID to use +;key_id = 0123456789ABCDEF0123456789ABCDEF01234567 diff --git a/examples/shared/config-local/keys-sync b/examples/shared/config-local/keys-sync new file mode 100644 index 0000000..5ec63cc --- /dev/null +++ b/examples/shared/config-local/keys-sync @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqatsYGwteC4X+aIsNThluXZlh9SbdQTB3Zy8EoRcgKSEQdvw +sn56e3SLCTtLVGYCD7l5bTvjuJIG0VGKGG8ZYiESHQRBqm6t0zWEJMiOC0mUt3xW +4YJcVFhsk54WhVMWW7Irjx+wk9VbX4OellU0FZSjAs+hSernAOnVoSNXnSaC5hoq +98QWdlUwI+pOkKfyII4YYcSaPByNA/dx4Hy4cVqIaVIvDf4SiYHnqbCCZbSZcrQD +W/pLnq6s4e3LvYNaB2BWLlbad8YuJRBRSp9C3thV1EmYIDQdlk6sg0fkFGYi+jWF +WKatA0u7Krt5Dh3BeeNgFqegfsb3E1P8+gUp6QIDAQABAoIBADdzCIYylGGUXs18 +jIoQFl6YLPJJL0z88waj9GrwyvJX0clcQbtzzj+OhOnNcP7yH3ZYHTDvw6pMPuIl +jcYSeO8y86J8A6HZbgF6mecTjChwMaQNhK9KVTZTd5h+r8l+r+3juoyZxLFrpuL+ +NtPWoKD05JlEled8V2ZbBTAWRsnahepwwIdVK9kroK/Z/O91YXHxsKkW8tr0BPAR +mJNFOZnMxKymyXZ05J9pEq+q054nDHdpOcKF6z1dNuVVpPvtPlJ+BfNZqLcQ87/3 +4AiX7hFEyiRQw6v5JXUXH1IkNuD3BpjFMAr5/g1damsWXw+rkeYk5uS44Em46LeU +hk2J8ykCgYEA1B9eZEI/xpejcXDLTR7NihF2kk3YmiHjnqvVRPV97/JlMw1kmulb +ga1Y4y7YLv0TT2lWTZ9U5d+aIGTLqDd2fFvUYwAojeibf8TIPxWj47IK1sG7K7/4 +fwtxzuYJ6TmehwKFLMqxW/RN8bxmXZOQyc0vdbWdc30filRpdJfkyMMCgYEAzMQG +NIc3WObmaNzT1qlC7svba0VWpE7Tve9yUlfQUzqiEPMWKxCxoxiSdjxKh3ZeFaGM +vb+EvvHryZVwO6Ym8YO3c7Or96/38dlpzR2USPPsE/z7q4Ai7p8s2OnpUdTN+Sbz +rUvWFZD+aTTzYaPHiknGGE4oTt90ksXhsHKR9+MCgYBZchzIZBMmd7zI0go4K9v5 +82EvUjoazF2CA+07VdVT/79ipqwckngul10a9x7buYWR/9YPhzcyc3Y3YKlr9m81 +Azfswx1WsZYYa28RAtwH4ISniCuXyCxNzhKgbuoQ+WX6gjqL57CgGMVhQNNddCMi +pS31Ea8iCsno9608J+ymJwKBgAupDgFnwcsaOvy1top+0QTwLuqI5EovAvfJ7uSp +g39500jLzvNc3ADBoXWCMWxNXUY2EOGtSk3lUNwF2oJLD+So74VzMdPE/YWUL+Yy +TQNqgIMS4PH/Nf4IqnEfwN2cFK6ffTjdHK/Vtecf0Mw6m15QnSs9KCZ1qxnAkv2N +SKqpAoGBAKb7H7PEoBlC0SfrdQIoqKcHX62IEVSS/bzeKRgOF8dNGeJkMLJeOzgK +uAdi8XYReKp6xkMz2KArfR55nkqGmqPHa9czxLcRh0afdccjK4kEgiCA3sGz80Hp +2r7EBnR6AK5aBTNDnpv2TQdOCTed5QBwHUWUWlD36xDayW/+cehH +-----END RSA PRIVATE KEY----- diff --git a/examples/shared/config-local/keys-sync.pub b/examples/shared/config-local/keys-sync.pub new file mode 100644 index 0000000..dce6e2a --- /dev/null +++ b/examples/shared/config-local/keys-sync.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@ska.example.com