Deploy Hexo to your own server using SSH key, and access it using Nginx

This has really been a hassle for me. My main server’s SSH port is not 22, and I couldn’t find any instructions on how to change the port. I tried using rsync, but it was too complicated to install on Windows…I didn’t have time to deal with accessing git over HTTP. In the end, I settled with using SFTP, but there are very few tutorials online, and the instructions are not clear. I took many wrong turns and wasted 2 hours before finally getting it to work. Ultimately, it was my own carelessness and incorrect permission settings that caused the issues. Here’s a brief summary of what I learned.(English version Translated by GPT-3.5, 返回中文)

Precautions to avoid pitfalls

  1. Ensure correct permissions: set the permissions of id_rsa and authorized_keys to 600, and the permissions of the .ssh directory to 700.
  2. Ensure that the deployment directory has been created.

Preparation

  1. Prepare a cloud server.
  2. Set up the user and group for Nginx. Skip the installation of Nginx for now. Here, let’s assume the user and group for Nginx are both “nginx.”
  3. Install npm and have Hexo ready for deployment. Skip the installation of npm and the initialization of Hexo for now.

Generating SSH keys on the server

In general, it is highly unrecommended to generate a root key. Instead, it is recommended to use a low privilege sub-user. That way, if anything goes wrong, only Hexo will be affected.

  1. First, create a new user, let’s call it “hexo”.

    1
    2
    3
    4
    [root@VM_0_7_centos nginx]# useradd -g nginx -s /bin/bash hexo
    [root@VM_0_7_centos nginx]#

    这表示创建一个隶属于nginx组的用户, -s是指指定用户登入后所使用的shell, 如果不希望用户能登陆, 可以指定/sbin/nologin
  2. Since we will be using SSH key authentication, there is no need to set a password. Log in to this user by running su hexo.

    1
    2
    [root@VM_0_7_centos nginx]# su hexo
    [hexo@VM_0_7_centos nginx]$
  3. Use ssh-keygen -t rsa to generate the SSH keys. You will go through the following steps.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    [hexo@VM_0_7_centos nginx]$ ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/hexo/.ssh/id_rsa): # 将密钥保存到括号的目录, 直接回车即可
    Created directory '/home/hexo/.ssh'.
    Enter passphrase (empty for no passphrase): # 输入密钥的密码(类似于pin, 只对改密钥生效, 输入过程不会显示)
    Enter same passphrase again: # 再次输入密钥的密码(依然不会显示)
    Your identification has been saved in /home/hexo/.ssh/id_rsa.
    Your public key has been saved in /home/hexo/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:bu2oc2gNB7QPN8eIMDz2rph/YieoMVKUSM3VLRXAFVo hexo@VM_0_7_centos
    The key's randomart image is:
    +---[RSA 2048]----+
    | .o...o.=Eo |
    |.. +* .+o. |
    |. o. * +.o |
    | . * + o |
    | . . =So |
    | . o.o. |
    |+ + . =o . |
    |.o+ = *.oo |
    |.. o.*.+. . |
    +----[SHA256]-----+
    [hexo@VM_0_7_centos nginx]$ # 创建完成
  4. You can see that the following files have been generated in the ~/.ssh directory.

    1
    2
    3
    4
    5
    6
    [hexo@VM_0_7_centos nginx]$ cd ~/.ssh/
    [hexo@VM_0_7_centos .ssh]$ ll
    total 8
    -rw------- 1 hexo nginx 1766 Jan 20 16:45 id_rsa
    -rw-r--r-- 1 hexo nginx 400 Jan 20 16:45 id_rsa.pub
    [hexo@VM_0_7_centos .ssh]$
  5. Copy the public key to the authorized_keys file by running the command cat id_rsa.pub >> authorized_keys.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    为什么一定要是authorized_keys的文件, 可以看到/etc/ssh/sshd_config的配置中定义了, 默认使用.ssh/authorized_keys的里存储的密钥信息, 当然也可以把它改成id_rsa.pub

    .
    .
    #LoginGraceTime 2m
    #PermitRootLogin yes
    #StrictModes yes
    #MaxAuthTries 6
    #MaxSessions 10

    #PubkeyAuthentication yes

    # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
    # but this is overridden so installations will only check .ssh/authorized_keys
    AuthorizedKeysFile .ssh/authorized_keys

    #AuthorizedPrincipalsFile none

    #AuthorizedKeysCommand none
    #AuthorizedKeysCommandUser nobody
    .
    .

  6. Change the permissions of authorized_keys to 600 by running chmod 600 authorized_keys. This is to prevent other users from having access to the public key.

    1
    2
    3
    4
    5
    6
    7
    8
    [hexo@VM_0_7_centos .ssh]$ ll
    total 12
    -rw-r--r-- 1 hexo nginx 400 Jan 20 16:50 authorized_keys # 可以看到authorized_keys默认是644的权限, 这样届时登陆会被拒绝掉的.
    -rw------- 1 hexo nginx 1766 Jan 20 16:45 id_rsa
    -rw-r--r-- 1 hexo nginx 400 Jan 20 16:45 id_rsa.pub
    # 更改为600
    [hexo@VM_0_7_centos .ssh]$ chmod 600 authorized_keys
    [hexo@VM_0_7_centos .ssh]$
  7. Create a new folder. This will be the folder where the Hexo content will be stored. Here, I created a folder named “/usr/hexoWeb” as root, and changed its permissions to allow read and write access for myself only, and read and execute access for others. I also changed the user group to hexo:nginx by running chown -R hexo:nginx /usr/hexoWeb.

  8. Edit the private key “id_rsa” and copy its content. You can directly copy it using vi, extract it when SSHing into the server, or use cat to display the content and then copy it. Create a new text file and paste the content into it. Save the file. At this point, the server-side configuration is complete. You can now delete the generated id_rsa and id_rsa.pub files on the server since you already have a copy of the private key and the public key is already in the authorized_keys file.

Configuring SFTP for Hexo

  1. I initialized a new Hexo project. It runs very quickly using hexo init. Here is the directory structure. I placed the key I copied earlier in the root directory, as shown in the “Last File”.

    hexo dir
    This is what the initial Hexo looks like (NexT is so scary… I really modified the two themes…)
    hexo page

  2. According to the official SFTP deployment method, you need to install a component called “hexo-deployer-sftp.” Change to the Hexo root directory by running cd /d, and then run npm install hexo-deployer-sftp --save. You can also use npm install cnpm, and then cnpm install hexo-deployer-sftp --save to speed it up. Alternatively, simply execute the command directly.

    1
    2
    3
    4
    5
    6
    7
    D:\hexoTest>cnpm install hexo-deployer-sftp --save
    √ Installed 1 packages
    √ Linked 19 latest versions
    √ Run 0 scripts
    anti semver hexo-deployer-sftp@0.1.0 › sftp-sync-deploy@0.5.0 › @types/ssh2-streams@0.1.4 › @types/node@* delcares @types/node@*(resolved as 10.12.18) but using ancestor(sftp-sync-deploy)'s dependency @types/node@^6.0.51(resolved as 6.14.2)
    anti semver hexo-deployer-sftp@0.1.0 › sftp-sync-deploy@0.5.0 › @types/ssh2@0.5.37 › @types/node@* delcares @types/node@*(resolved as 10.12.18) but using ancestor(sftp-sync-deploy)'s dependency @types/node@^6.0.51(resolved as 6.14.2)
    √ All packages installed (20 packages installed from npm registry, used 501ms(network 489ms), speed 150.21kB/s, json 20(73.46kB), tarball 0B)
  3. Configure SFTP. The example configuration for SFTP looks like this. You can see that the required fields are type, host, and user.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Example:
    deploy:
    type: sftp
    host: <host>
    port: [port] # Default is 21 # 注意, 一般的ssh是22, 估计是官方打错了, 源码默认是22的(21是FTP端口)
    user: <user>
    pass: <pass> # leave blank for paswordless connections
    privateKey: [path/to/privateKey] # Optional
    passphrase: [passphrase] # Optional
    agent: [path/to/agent/socket] # Optional, defaults to $SSH_AUTH_SOCK
    remotePath: [remotePath] # Default is `/`
  4. Finally, my configuration looks like this:

    1
    2
    3
    4
    5
    6
    7
    deploy:
    type: sftp
    host: 118.*.*.219
    user: hexo
    privateKey: privatekey.txt
    remotePath: /usr/hexoWeb
    passphrase: testtest # 如果没有就不填
  5. Finally, run hexo hexo d (or hexo deploy) to deploy the project. It should go smoothly.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    D:\hexoTest>hexo d
    INFO Start processing
    INFO Files loaded in 266 ms
    INFO Generated: index.html
    INFO Generated: archives/index.html
    INFO Generated: fancybox/blank.gif
    INFO Generated: fancybox/jquery.fancybox.css
    INFO Generated: fancybox/fancybox_loading@2x.gif
    INFO Generated: fancybox/fancybox_sprite.png
    INFO Generated: fancybox/fancybox_overlay.png
    INFO Generated: archives/2019/01/index.html
    INFO Generated: fancybox/helpers/fancybox_buttons.png
    INFO Generated: fancybox/fancybox_loading.gif
    INFO Generated: archives/2019/index.html
    INFO Generated: fancybox/fancybox_sprite@2x.png
    INFO Generated: css/fonts/FontAwesome.otf
    INFO Generated: js/script.js
    INFO Generated: fancybox/jquery.fancybox.pack.js
    INFO Generated: css/style.css
    INFO Generated: fancybox/helpers/jquery.fancybox-buttons.css
    INFO Generated: fancybox/helpers/jquery.fancybox-media.js
    INFO Generated: fancybox/helpers/jquery.fancybox-buttons.js
    INFO Generated: fancybox/helpers/jquery.fancybox-thumbs.js
    INFO Generated: css/fonts/fontawesome-webfont.eot
    INFO Generated: fancybox/helpers/jquery.fancybox-thumbs.css
    INFO Generated: css/fonts/fontawesome-webfont.woff
    INFO Generated: css/images/banner.jpg
    INFO Generated: css/fonts/fontawesome-webfont.ttf
    INFO Generated: css/fonts/fontawesome-webfont.svg
    INFO Generated: 2019/01/20/hello-world/index.html
    INFO Generated: fancybox/jquery.fancybox.js
    INFO 28 files generated in 550 ms
    INFO Deploying: sftp
    * Deploying to host 118.*.*.219
    * local dir = D:\hexoTest\public
    * remote dir = /usr/hexoWeb

    file uploaded : index.html
    file uploaded : js/script.js
    sync completed : js
    file uploaded : archives/index.html
    file uploaded : css/style.css
    file uploaded : fancybox/blank.gif
    file uploaded : fancybox/fancybox_overlay.png
    file uploaded : fancybox/fancybox_sprite@2x.png
    file uploaded : fancybox/fancybox_loading@2x.gif
    file uploaded : fancybox/fancybox_sprite.png
    file uploaded : fancybox/fancybox_loading.gif
    file uploaded : fancybox/jquery.fancybox.pack.js
    file uploaded : fancybox/jquery.fancybox.css
    file uploaded : fancybox/jquery.fancybox.js
    file uploaded : fancybox/helpers/fancybox_buttons.png
    file uploaded : fancybox/helpers/jquery.fancybox-buttons.css
    file uploaded : fancybox/helpers/jquery.fancybox-buttons.js
    file uploaded : fancybox/helpers/jquery.fancybox-media.js
    file uploaded : fancybox/helpers/jquery.fancybox-thumbs.css
    file uploaded : fancybox/helpers/jquery.fancybox-thumbs.js
    sync completed : fancybox/helpers
    sync completed : fancybox
    file uploaded : css/fonts/fontawesome-webfont.eot
    file uploaded : css/fonts/fontawesome-webfont.svg
    file uploaded : css/fonts/fontawesome-webfont.woff
    file uploaded : css/fonts/fontawesome-webfont.ttf
    file uploaded : css/fonts/FontAwesome.otf
    sync completed : css/fonts
    file uploaded : css/images/banner.jpg
    sync completed : css/images
    sync completed : css
    file uploaded : archives/2019/index.html
    file uploaded : archives/2019/01/index.html
    sync completed : archives/2019/01
    sync completed : archives/2019
    sync completed : archives
    file uploaded : 2019/01/20/hello-world/index.html
    sync completed : 2019/01/20/hello-world
    sync completed : 2019/01/20
    sync completed : 2019/01
    sync completed : 2019
    INFO Deploy done: sftp

    D:\hexoTest>
  6. Go to the server and you will see that the files have been uploaded.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [hexo@VM_0_7_centos hexoWeb]$ pwd
    /usr/hexoWeb
    [hexo@VM_0_7_centos hexoWeb]$ ll
    total 28
    drwxr-xr-x 3 hexo nginx 4096 Jan 20 17:32 2019
    drwxr-xr-x 3 hexo nginx 4096 Jan 20 17:32 archives
    drwxr-xr-x 4 hexo nginx 4096 Jan 20 17:32 css
    drwxr-xr-x 3 hexo nginx 4096 Jan 20 17:32 fancybox
    -rw-r--r-- 1 hexo nginx 6659 Jan 20 17:33 index.html
    drwxr-xr-x 2 hexo nginx 4096 Jan 20 17:32 js
    [hexo@VM_0_7_centos hexoWeb]$

Configuring Nginx

  1. Open the nginx.conf file and add the following configuration:

    img

  2. Access the IP address and you will see that it works.

    img

  3. Try posting an article with the title and content as “This is a new article,” and then deploy it.

    1
    2
    3
    4
    D:\hexoTest>hexo new 这是新的文章
    INFO Created: D:\hexoTest\source\_posts\这是新的文章.md

    D:\hexoTest>
  4. Force refresh the page with Ctrl + F5, and you will see that it has been deployed.

    img

Common Errors

  1. Error: Connection Error: All configured authentication methods failed.

    This error indicates that the information provided in the configuration is incorrect and cannot connect to the remote server using the provided details. Check each parameter in the configuration and try again. It’s also a good idea to check the server logs by running tail -f /var/log/secure to see if there are any login logs. Regardless of whether the login attempt was successful or not, there should be a log entry. This will help you eliminate any network issues.

    • Ensure that the .ssh directory on the server has the correct permissions (700), and the authorized_keys file has the correct permissions (600). (It seems that 655 permissions also work, meaning that other users except the owner of the .ssh directory should not have write permissions).
    • Make sure that the username provided matches the one used to generate the SSH key.
    • If you haven’t deleted the id_rsa file on the server, you can test the key by running ssh -i id_rsa username@127.0.0.1 -p 22 to test if the key can connect to the server. This will help you pinpoint any server-side configuration errors.
    • Check if you have misspelled “authorized_keys,” and also check if the file specified after “AuthorizedKeysFile” in the /etc/ssh/sshd_config file is pointing to the correct file.
  2. Error: Remote Error: No such directory ****.

    This error indicates that the specified directory does not exist. Create the directory first.

  3. InvalidAsn1Error: encoding too long.

    This error occurs when the key password entered is incorrect.

  4. error:0606508A:digital envelope routines:EVP_DecryptFinal_ex:data not multiple of block length.

    This error occurs when there is missing or extra content when copying the key.

  5. Remote Error: Cannnot create directory. Permission denied /**** (any content with “Permission denied”).

    This error occurs when the current user does not have permission to create files in that directory. If you are creating a file outside the user’s home directory, ensure that the folder has rwx permissions (7), and that the folder belongs to the current user.