Skip to content

Commit

Permalink
add SSH to advanced settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Pastusek authored and Dan Pastusek committed Mar 16, 2024
1 parent aa5ea79 commit 8435ad5
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 18 deletions.
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "pibox-host",
"version": "1.42.0",
"version": "1.43.0",
"private": true,
"bin": "server.js",
"scripts": {
Expand Down
39 changes: 22 additions & 17 deletions src/components/Header.js
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser } from '@fortawesome/free-solid-svg-icons'
import { faUser, faGear } from '@fortawesome/free-solid-svg-icons'
import { fetchApi } from '@/ui-functions'

export default function Header({ loadUser = true }) {
Expand Down Expand Up @@ -30,22 +30,27 @@ export default function Header({ loadUser = true }) {
<Link href="/" className="text-3xl font-semibold">
PiBox
</Link>
<div>
{loading ? (
<>...</>
) : whoami ? (
<div className="mr-4">
<FontAwesomeIcon icon={faUser} className="mr-1" />
{whoami?.linuxUser}
<Link href="/logout" className="ml-4">
Logout
</Link>
</div>
) : (
<div>
<Link href="/login">Login</Link>
</div>
)}
<div className="flex items-center">
<div>
{loading ? (
<>...</>
) : whoami ? (
<div>
<FontAwesomeIcon icon={faUser} className="mr-1" />
{whoami?.linuxUser}
<Link href="/logout" className="ml-4">
Logout
</Link>
</div>
) : (
<div>
<Link href="/login">Login</Link>
</div>
)}
</div>
<Link href="/settings" className="ml-4">
<FontAwesomeIcon icon={faGear} className="mr-1" />
</Link>
</div>
</div>
</div>
Expand Down
69 changes: 69 additions & 0 deletions src/pages/api/ssh.js
@@ -0,0 +1,69 @@
import { execAsync, middlewareAuth } from '@/functions'
import { readFile, writeFile } from 'fs/promises'

export default async function handler(req, res) {
if (!(await middlewareAuth(req, res))) {
return res.status(401).json({ message: 'Unauthorized' })
}

if (!req.isOwner) {
return res.status(400).json({ message: 'Only the owner can change SSH settings' })
}

if (req.method === 'GET') {
getSettings(req, res)
} else {
updateSettings(req, res)
}
}

async function getSettings(req, res) {
const config = await readFile('/etc/ssh/sshd_config', 'utf-8')
const passwordLogin = config.includes('PasswordAuthentication yes')
let enabled = false
try {
const { stdout } = await execAsync('systemctl is-enabled ssh')
if (stdout.includes('enabled')) enabled = true
} catch {}
res.status(200).json({ enabled, passwordLogin })
}

async function updateSettings(req, res) {
const { enabled, passwordLogin } = req.body
if (typeof enabled !== 'boolean' || typeof passwordLogin !== 'boolean') {
return res.status(400).json({ message: 'Invalid request' })
}

let wasEnabled = false
try {
const { stdout } = await execAsync('systemctl is-enabled ssh')
if (stdout.includes('enabled')) wasEnabled = true
} catch {}

const config = await readFile('/etc/ssh/sshd_config', 'utf-8')
const newConfig = config
.split('\n')
.map((line) => {
if (line.includes('PasswordAuthentication')) {
return `PasswordAuthentication ${passwordLogin ? 'yes' : 'no'}`
}
return line
})
.join('\n')
await writeFile('/etc/ssh/sshd_config', newConfig)

if (wasEnabled && enabled) {
console.log(`Restarting SSH service`)
await execAsync('systemctl restart ssh')
} else if (enabled) {
console.log(`Enabling and starting SSH service`)
await execAsync('systemctl enable ssh')
await execAsync('systemctl start ssh')
} else {
console.log(`Disabling and stopping SSH service`)
await execAsync('systemctl disable ssh')
await execAsync('systemctl stop ssh')
}

res.status(200).json({ enabled, passwordLogin })
}
66 changes: 66 additions & 0 deletions src/pages/settings.js
@@ -0,0 +1,66 @@
import { useEffect, useState } from 'react'
import { fetchApi } from '@/ui-functions'
import Header from '@/components/Header'

export default function Home() {
const [loading, setLoading] = useState(true)
const [enabled, setEnabled] = useState([])
const [passwordLogin, setPasswordLogin] = useState([])

async function whoami() {
if (typeof window === 'undefined') return
const { body } = await fetchApi('/api/whoami')
if (body.piboxConfigUser) {
getSshSettings()
} else {
window.location.href = '/login'
}
}

async function getSshSettings() {
if (typeof window === 'undefined') return
const { body } = await fetchApi('/api/ssh')
setLoading(false)
setEnabled(body.enabled)
setPasswordLogin(body.passwordLogin)
}

async function updateSshSettings({ enabled, passwordLogin }) {
setLoading(true)
setEnabled(enabled)
setPasswordLogin(passwordLogin)
if (typeof window === 'undefined') return
await fetchApi('/api/ssh', {
method: 'POST',
body: JSON.stringify({ enabled, passwordLogin }),
})
setLoading(false)
}

useEffect(() => getSshSettings(), [])

return (
<>
<main className="">
<Header />
<section className="px-8 pt-4 container mx-auto">
<h1 className="text-3xl font-semibold mb-4 mt-2">System Settings</h1>
{loading ? (
<p>Loading...</p>
) : (
<>
<label className="block mb-4">
<input className="mr-2" type="checkbox" checked={enabled} onChange={(e) => updateSshSettings({ enabled: e.target.checked, passwordLogin })} />
Enable SSH
</label>
<label>
<input className="mr-2" type="checkbox" checked={passwordLogin} onChange={(e) => updateSshSettings({ enabled, passwordLogin: e.target.checked })} />
Allow Password login
</label>
</>
)}
</section>
</main>
</>
)
}

0 comments on commit 8435ad5

Please sign in to comment.