Content dump and hide apprentice docs for now...
This commit is contained in:
parent
9d95802629
commit
193b7324f7
135
assets/apprentice/ansible/ansible.cfg
Normal file
135
assets/apprentice/ansible/ansible.cfg
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
[defaults]
|
||||||
|
roles_path = ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
||||||
|
library = ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
|
||||||
|
inventory = ~/.ansible/hosts,/etc/ansible/hosts
|
||||||
|
|
||||||
|
collections_path = ~/.ansible/collections:/usr/share/ansible/collections
|
||||||
|
# This may be "ignore" or "fail", "warnging" is the default
|
||||||
|
collections_on_ansible_version_mismatch = warning
|
||||||
|
collections_scan_sys_path = True
|
||||||
|
|
||||||
|
# Very useful, this will issue a warning if you run a command that
|
||||||
|
# Ansible thinks there is an existing module to do the same job!
|
||||||
|
command_warnings = True
|
||||||
|
forks = 42
|
||||||
|
|
||||||
|
# The default for gathering is "impicit", meaning Ansible won't cache facts
|
||||||
|
gathering = smart
|
||||||
|
gather_timeout = 13
|
||||||
|
|
||||||
|
# May specify "paramiko", "ssh" or "smart" for choosing based of OS and ssh versions
|
||||||
|
transport = smart
|
||||||
|
use_persistent_connections = True
|
||||||
|
private_key_file = ~/.ssh/id_rsa
|
||||||
|
pipelining = ANSIBLE_PIPELINING
|
||||||
|
|
||||||
|
# Task timeout, 0 = no timeout
|
||||||
|
task_timeout = 0
|
||||||
|
# Connection timeout
|
||||||
|
timeout = 7
|
||||||
|
|
||||||
|
# Run plays as fast as possible
|
||||||
|
strategy = free
|
||||||
|
|
||||||
|
# This gives better performance at the expense of CPU load,
|
||||||
|
# so I guess you'd want it low on your workstation or laptop,
|
||||||
|
# but higher on like an Ansible tower or similar automation setup using Ansible
|
||||||
|
internal_poll_interval = 0.000001
|
||||||
|
poll_interval = 3
|
||||||
|
|
||||||
|
system_tmpdirs = /tmp, /var/tmp
|
||||||
|
remote_tmp = ~/.ansible/tmp
|
||||||
|
local_tmp = ~/.ansible/tmp
|
||||||
|
|
||||||
|
jinja2_native_warning = True
|
||||||
|
jinja2_native = True
|
||||||
|
|
||||||
|
# This one is only really useful to make sure no secrets are logged on servers
|
||||||
|
# when running Ansible tower or other automation systems using Ansible
|
||||||
|
no_log = False
|
||||||
|
# Logging is disabled on the client if "log_path" is empty
|
||||||
|
log_path =
|
||||||
|
# Target hosts logging facility
|
||||||
|
syslog_facility = LOG_USER
|
||||||
|
no_target_syslog = False
|
||||||
|
|
||||||
|
# Must be one from python [zipfile](https://docs.python.org/3/library/zipfile.html) built-in library that is supported
|
||||||
|
# on both the client and the target servers Ansible is going to run on, ZIP_DEFLATED is the default
|
||||||
|
module_compression = ZIP_LZMA
|
||||||
|
# ZIP_STORED = no compression
|
||||||
|
# ZIP_DEFLATED = normal zip, requires zlib python module
|
||||||
|
# ZIP_BZIP2 = bzip2, requires bz2 python module
|
||||||
|
# ZIP_LZMA = lzma, requires lzma python module
|
||||||
|
|
||||||
|
# Default module to run with the "ansible" command if not -m [module] is specified
|
||||||
|
module_name = command
|
||||||
|
|
||||||
|
# What will null variables in templates show?
|
||||||
|
null_representation =
|
||||||
|
|
||||||
|
# Let's make some errors warnings instead
|
||||||
|
invalid_task_attribute_failed = False
|
||||||
|
error_on_missing_handler = False
|
||||||
|
string_conversion_action = warn
|
||||||
|
|
||||||
|
# The same as the -v command line arg
|
||||||
|
verbosity = 0
|
||||||
|
|
||||||
|
# Extra output goodness!~
|
||||||
|
show_task_path_on_failure = True
|
||||||
|
display_args_to_stdout = True
|
||||||
|
display_skipped_hosts = True
|
||||||
|
show_per_host_start = True
|
||||||
|
check_mode_markers = True
|
||||||
|
show_custom_stats = True
|
||||||
|
display_ok_hosts = True
|
||||||
|
|
||||||
|
# This may be useful
|
||||||
|
retry_files_enabled = False
|
||||||
|
|
||||||
|
yaml_valid_extensions = .yml, .yaml, .json
|
||||||
|
|
||||||
|
display_failed_stderr = True
|
||||||
|
|
||||||
|
[persistent_connection]
|
||||||
|
command_timeout = 42
|
||||||
|
connect_retry_timeout = 7
|
||||||
|
connect_timeout = 60
|
||||||
|
|
||||||
|
[connection]
|
||||||
|
# This only works if your become supports not using a tty,
|
||||||
|
# but gives "significant performance improvement when enabled."
|
||||||
|
pipelining = True
|
||||||
|
|
||||||
|
# Eye-candy!
|
||||||
|
[colors]
|
||||||
|
changed = bright yellow
|
||||||
|
console_prompt = white
|
||||||
|
debug = gray
|
||||||
|
deprecate = purple
|
||||||
|
diff_add = green
|
||||||
|
diff_lines = cyan
|
||||||
|
diff_remove = red
|
||||||
|
error = red
|
||||||
|
highlight = white
|
||||||
|
ok = green
|
||||||
|
skip = cyan
|
||||||
|
unreachable = bright red
|
||||||
|
verbose = blue
|
||||||
|
warn = bright purple
|
||||||
|
|
||||||
|
[selinux]
|
||||||
|
# Enable this if running SELinux
|
||||||
|
libvirt_lxc_noseclabel = False
|
||||||
|
# We want this to be empty unless maybe nfs target? it may be a list of filesystems
|
||||||
|
special_context_filesystems =
|
||||||
|
|
||||||
|
[diff]
|
||||||
|
always = True
|
||||||
|
context = 3
|
||||||
|
|
||||||
|
[inventory]
|
||||||
|
enable_plugins = host_list, script, auto, yaml, ini, toml
|
||||||
|
|
||||||
|
[paramiko_connection]
|
||||||
|
host_key_auto_add = True
|
0
assets/apprentice/ansible/docker.ini
Normal file
0
assets/apprentice/ansible/docker.ini
Normal file
56
assets/apprentice/ansible/inv.ini
Normal file
56
assets/apprentice/ansible/inv.ini
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
[prod]
|
||||||
|
lost-islands
|
||||||
|
|
||||||
|
[prod:children]
|
||||||
|
prod_frontend
|
||||||
|
prod_backend
|
||||||
|
prod_vpn
|
||||||
|
prod_db
|
||||||
|
monitor
|
||||||
|
backups
|
||||||
|
misc
|
||||||
|
|
||||||
|
[prod_frontend]
|
||||||
|
hamar
|
||||||
|
toten
|
||||||
|
|
||||||
|
[prod_backend]
|
||||||
|
lofoten
|
||||||
|
narvik
|
||||||
|
|
||||||
|
[prod_vpn]
|
||||||
|
bergen
|
||||||
|
molde
|
||||||
|
|
||||||
|
[prod_db]
|
||||||
|
hangar-22
|
||||||
|
|
||||||
|
[monitor]
|
||||||
|
smokeping.skyid.no
|
||||||
|
monitoring
|
||||||
|
|
||||||
|
[backups]
|
||||||
|
backup.skyid.no
|
||||||
|
myrkdalen
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
gulf-of-oman
|
||||||
|
deploy
|
||||||
|
|
||||||
|
[dev:children]
|
||||||
|
dev_frontend
|
||||||
|
dev_backend
|
||||||
|
dev_vpn
|
||||||
|
dev_db
|
||||||
|
|
||||||
|
[dev_frontend]
|
||||||
|
mercury
|
||||||
|
|
||||||
|
[dev_backend]
|
||||||
|
scrapmetal
|
||||||
|
|
||||||
|
[dev_vpn]
|
||||||
|
dawnbreaker
|
||||||
|
|
||||||
|
[dev_db]
|
||||||
|
propaganda
|
67
assets/apprentice/ansible/inv.py
Normal file
67
assets/apprentice/ansible/inv.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
|
inv = {
|
||||||
|
'prod_frontend': {
|
||||||
|
'hosts': ['toten', 'hamar']
|
||||||
|
}, 'prod_backend': {
|
||||||
|
'hosts': ['lofoten', 'narvik']
|
||||||
|
}, 'prod_vpn': { # vpn/radius
|
||||||
|
'hosts': ['bergen', 'molde']
|
||||||
|
}, 'prod_db': {
|
||||||
|
'hosts': ['hangar-22']
|
||||||
|
}, 'prod': {
|
||||||
|
'hosts': ['lost-islands'], # monitoring and internal DNS
|
||||||
|
'children': [
|
||||||
|
'prod_frontend', 'prod_backend', 'misc',
|
||||||
|
'prod_vpn', 'prod_db', 'backups', 'monitor'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'misc': {
|
||||||
|
'hosts': ['gulf-of-oman', 'deploy']
|
||||||
|
}, 'backups': {
|
||||||
|
'hosts': ['myrkdalen', 'backup.skyid.no']
|
||||||
|
}, 'monitor': {
|
||||||
|
'hosts': ['monitoring', 'smokeping.skyid.no']
|
||||||
|
},
|
||||||
|
'dev_frontend': { 'hosts': ['mercury'] },
|
||||||
|
'dev_backend': { 'hosts': ['scrapmetal'] },
|
||||||
|
'dev_vpn': { 'hosts': ['dawnbreaker'] },
|
||||||
|
'dev_db': { 'hosts': ['propaganda'] },
|
||||||
|
'dev': {
|
||||||
|
'children': [
|
||||||
|
'dev_frontend', 'dev_backend', 'dev_vpn', 'dev_db'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
# since we're not doing hostvars this will make it a whole lot faster
|
||||||
|
# as Ansible won't have to run this script with --host for each one
|
||||||
|
"hostvars": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def inventory():
|
||||||
|
return dumps(inv, indent=2)
|
||||||
|
|
||||||
|
def group_vars(host):
|
||||||
|
for group in inv.values():
|
||||||
|
if host in group.get('hosts', []):
|
||||||
|
return dumps(group.get('vars', {}), indent=2)
|
||||||
|
return '{}'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
argparse = ArgumentParser()
|
||||||
|
argparse.add_argument(
|
||||||
|
'-l', '--list', action='store_true',
|
||||||
|
help='Print the inventory to stdout')
|
||||||
|
argparse.add_argument(
|
||||||
|
'-v', '--host', type=str,
|
||||||
|
help='Print host vars for a specific host')
|
||||||
|
args = argparse.parse_args()
|
||||||
|
if args.list:
|
||||||
|
print(inventory())
|
||||||
|
elif args.host:
|
||||||
|
print(group_vars(args.host))
|
||||||
|
else:
|
||||||
|
argparse.print_help()
|
18
assets/apprentice/ansible/inv.txt
Normal file
18
assets/apprentice/ansible/inv.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
lost-islands
|
||||||
|
toten
|
||||||
|
hamar
|
||||||
|
lofoten
|
||||||
|
narvik
|
||||||
|
gulf-of-oman
|
||||||
|
deploy
|
||||||
|
bergen
|
||||||
|
molde
|
||||||
|
hangar-22
|
||||||
|
myrkdalen
|
||||||
|
backup.skyid.no
|
||||||
|
monitoring
|
||||||
|
smokeping.skyid.no
|
||||||
|
mercury
|
||||||
|
scrapmetal
|
||||||
|
dawnbreaker
|
||||||
|
propaganda
|
47
assets/apprentice/ansible/inv.yml
Normal file
47
assets/apprentice/ansible/inv.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
all:
|
||||||
|
children:
|
||||||
|
prod:
|
||||||
|
hosts:
|
||||||
|
lost-islands:
|
||||||
|
children:
|
||||||
|
prod_frontend:
|
||||||
|
hosts:
|
||||||
|
hamar:
|
||||||
|
toten:
|
||||||
|
prod_backend:
|
||||||
|
hosts:
|
||||||
|
lofoten:
|
||||||
|
narvik:
|
||||||
|
prod_vpn:
|
||||||
|
hosts:
|
||||||
|
bergen:
|
||||||
|
molde:
|
||||||
|
prod_db:
|
||||||
|
hosts:
|
||||||
|
hangar-22:
|
||||||
|
monitor:
|
||||||
|
hosts:
|
||||||
|
smokeping.skyid.no:
|
||||||
|
monitoring:
|
||||||
|
backups:
|
||||||
|
hosts:
|
||||||
|
backup.skyid.no:
|
||||||
|
myrkdalen:
|
||||||
|
misc:
|
||||||
|
hosts:
|
||||||
|
gulf-of-oman:
|
||||||
|
deploy:
|
||||||
|
dev:
|
||||||
|
children:
|
||||||
|
dev_frontend:
|
||||||
|
hosts:
|
||||||
|
mercury:
|
||||||
|
dev_backend:
|
||||||
|
hosts:
|
||||||
|
scrapmetal:
|
||||||
|
dev_vpn:
|
||||||
|
hosts:
|
||||||
|
dawnbreaker:
|
||||||
|
dev_db:
|
||||||
|
hosts:
|
||||||
|
propaganda:
|
8
assets/apprentice/security/accesscontrol-fix.py
Normal file
8
assets/apprentice/security/accesscontrol-fix.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class AccessControl:
|
||||||
|
current_user = None
|
||||||
|
...
|
||||||
|
def get_current_user(self):
|
||||||
|
if self.current_user.deleted is None:
|
||||||
|
return self.current_user
|
||||||
|
else:
|
||||||
|
raise Forbidden
|
5
assets/apprentice/security/accesscontrol.py
Normal file
5
assets/apprentice/security/accesscontrol.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AccessControl:
|
||||||
|
current_user = None
|
||||||
|
...
|
||||||
|
def get_current_user(self):
|
||||||
|
return self.current_user
|
2
assets/apprentice/security/htaccess
Normal file
2
assets/apprentice/security/htaccess
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Options +ExecCGI
|
||||||
|
AddHandler cgi-script .cgi .pl .py .sh .rb
|
@ -1,6 +1,5 @@
|
|||||||
.chroma {
|
.chroma { overflow: auto; color: #ffffff; background-color: #111111; }
|
||||||
min-height: 3.3vh; overflow: auto;
|
|
||||||
color: #ffffff; background-color: #111111; }
|
|
||||||
.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0 }
|
.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0 }
|
||||||
.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0 }
|
.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0 }
|
||||||
.chroma .hl { background-color: #ffffcc }
|
.chroma .hl { background-color: #ffffcc }
|
||||||
|
BIN
assets/img/aldri-ikke-gassed.jpg
Normal file
BIN
assets/img/aldri-ikke-gassed.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
assets/img/toeyen-holding-2.jpg
Normal file
BIN
assets/img/toeyen-holding-2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
BIN
assets/img/toeyen-holding.jpg
Normal file
BIN
assets/img/toeyen-holding.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 241 KiB |
BIN
assets/me.jpg
Normal file
BIN
assets/me.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 388 KiB |
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
date: 2022-04-05T18:33:08Z
|
date: 2022-04-05T18:33:08Z
|
||||||
draft: false
|
draft: true
|
||||||
aliases: []
|
aliases: []
|
||||||
categories: ['apprentice', 'indices', 'meta']
|
categories: ['apprentice', 'indices', 'meta']
|
||||||
series: ['apprentice']
|
series: ['apprentice']
|
||||||
|
@ -2,10 +2,103 @@
|
|||||||
date: 2022-06-07T07:15:24Z
|
date: 2022-06-07T07:15:24Z
|
||||||
draft: true
|
draft: true
|
||||||
aliases: []
|
aliases: []
|
||||||
categories: ['various']
|
categories: ['documentation']
|
||||||
series: []
|
series: ['apprentice']
|
||||||
tags: ['various']
|
tags: ['tools', 'tech']
|
||||||
|
chroma: true
|
||||||
toc: true
|
toc: true
|
||||||
title: Ansible
|
title: Ansible
|
||||||
description:
|
description: Ansible quickstart guide!
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Ansible is a cool tool that lets you manage lots of Linux servers and even some other devices.
|
||||||
|
All with just a bunch of yaml.
|
||||||
|
Although not necessarily as it supports a nice load of formats for everything.
|
||||||
|
Yaml being the most common, but "inventories" are usually ini format.
|
||||||
|
But still you should be able to do everything in json if you really want to.
|
||||||
|
|
||||||
|
## Configure ansible.cfg
|
||||||
|
This is the main Ansible config.
|
||||||
|
Ansible lets you have global stuff in /etc/ansible or project stuff in the working directory.
|
||||||
|
Anyway you'll likely want an ansible.cfg with your common global configuration options for Ansible itself.
|
||||||
|
|
||||||
|
### Example global ansible.cfg
|
||||||
|
This example ansibe.cfg lists a collection of the options I think are useful.
|
||||||
|
|
||||||
|
You may also generate a file with all options available commented out.
|
||||||
|
With comments describing them for most of the options by;
|
||||||
|
{{< highlight sh "lineNos=none" >}}ansible-config init --disabled -t all > ansible.cfg{{< /highlight >}}
|
||||||
|
|
||||||
|
{{< highlight cfg >}}{{% asset "apprentice/ansible/ansible.cfg" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
### Example project ansible.cfg
|
||||||
|
{{< highlight cfg >}}[default]
|
||||||
|
private_key_file = ~/.ssh/ansible_rsa
|
||||||
|
remote_user = ansible
|
||||||
|
inventory = all-machines
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
become = yes
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
## Inventory
|
||||||
|
I think it's super neat that Ansible lets you use several file formats for most things.
|
||||||
|
Inventories being the one that supports most formats, I'm pretty sure.
|
||||||
|
As you may just have a text file list, or YAML, INI, TOML and JSON to use host groups.
|
||||||
|
|
||||||
|
You may even have scripts that generate the target hosts with groups and everything.
|
||||||
|
If you opt for the script inventory route that script has to output the inventory in JSON format.
|
||||||
|
There is also the possibility of creating your own inventory plugin with python.
|
||||||
|
|
||||||
|
For the script based inventory you have to support command-line arguments "--list" and "--host <hostname>".
|
||||||
|
Those have to output the whole inventory in JSON format for "--list".
|
||||||
|
And the host vars for "--host <hostname>".
|
||||||
|
|
||||||
|
### The simplest inventories
|
||||||
|
These are of course just lists of hosts, they could be in a text file or specified from the command-line as a comma separated list.
|
||||||
|
Then you've got INI file based inventories, and it quickly may become advanced after that.
|
||||||
|
Even with the INI based ones you may group hosts together.
|
||||||
|
|
||||||
|
Next up is three examples that effectively gives the same inventory.
|
||||||
|
Although the text file won't give host groups.
|
||||||
|
|
||||||
|
#### A text file inventory list
|
||||||
|
{{< highlight txt >}}{{% asset "apprentice/ansible/inv.txt" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
#### INI inventory
|
||||||
|
Now host groups come into the picture and the rest of the examples will all give the same inventory.
|
||||||
|
|
||||||
|
{{< highlight ini >}}{{% asset "apprentice/ansible/inv.ini" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
### Example YAML inventory
|
||||||
|
This one will give you the exact inventory as above.
|
||||||
|
|
||||||
|
{{< highlight yml >}}{{% asset "apprentice/ansible/inv.yml" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
### Example python inventory script
|
||||||
|
This one is kinda just stupid as it just json dumps a dict containing the whole inventory.
|
||||||
|
To do some real magic with this you'd want to make that whole dict generated by code.
|
||||||
|
Very useful if for some reason your inventory is highly dynamic.
|
||||||
|
|
||||||
|
{{< highlight py >}}{{% asset "apprentice/ansible/inv.py" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
## Inventory summary
|
||||||
|
So the takeaway in my opinion is that INI or TOML based inventories are best.
|
||||||
|
As the YAML based ones are no fun working with.
|
||||||
|
JSON is also not optimal to manually work with, but a script generating that JSON may be very useful.
|
||||||
|
Again if you have the need of a potentially very dynamic inventory.
|
||||||
|
|
||||||
|
There is also the inventory parameters, these let you override lots of settings on a per-host basis.
|
||||||
|
Here is a super simple inventory to test plays/playbooks against a docker container;
|
||||||
|
|
||||||
|
{{< highlight ini >}}[docker]
|
||||||
|
docker-test ansible_connection=docker ansible_user=root ansible_host=ansible
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
You may use that inventory and run plays agains whatever container is named "ansible".
|
||||||
|
Or change the "ansible_host" parameter to the name of whatever container you'd like to use.
|
||||||
|
|
||||||
|
These optional parameters are "ansible_" something.
|
||||||
|
And you may control everything from connection and
|
||||||
|
|
||||||
|
INVENTORY PARAMETERS!
|
||||||
|
13
content/blog/apprentice/azure-ad.en.md
Normal file
13
content/blog/apprentice/azure-ad.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:11:01Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Azure AD
|
||||||
|
description: Azure Active Directory basics
|
||||||
|
---
|
||||||
|
|
13
content/blog/apprentice/cpanel.en.md
Normal file
13
content/blog/apprentice/cpanel.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:07:02Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: cPanel
|
||||||
|
description: cPanel is a server interface provided by many hosting providers for web based management of the servers by their customers
|
||||||
|
---
|
||||||
|
|
13
content/blog/apprentice/debian.en.md
Normal file
13
content/blog/apprentice/debian.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:10:48Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Debian
|
||||||
|
description: Systems administration stuff relevant to Debian spesifically
|
||||||
|
---
|
||||||
|
|
13
content/blog/apprentice/docker-fix.en.md
Normal file
13
content/blog/apprentice/docker-fix.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-13T08:42:53Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Docker Fix
|
||||||
|
description: The adventure I went through with fixing a terrible docker-compose local development setup
|
||||||
|
---
|
||||||
|
|
18
content/blog/apprentice/docker.en.md
Normal file
18
content/blog/apprentice/docker.en.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-11T17:33:55Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['docker']
|
||||||
|
series: ['apprentice']
|
||||||
|
tags: ['programming', 'devops', 'docker']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Docker
|
||||||
|
description: Introduction to Docker for local development and production deployment ready images.
|
||||||
|
---
|
||||||
|
|
||||||
|
Docker is a super container management system that let's you isolate programs and services running on servers.
|
||||||
|
This is very useful, both during development and for production deployment.
|
||||||
|
Because all dependencies may be bundled into the "docker image", and if the program/service is hacked.
|
||||||
|
The adversary will only have access to the container.
|
||||||
|
Which with proper configuration
|
@ -10,4 +10,29 @@ title: My terrible Apprentice experience!
|
|||||||
description: Here I'm going to write about my terrible time as an IT apprentice in two different small Norwegian companies. First one does web hosting, second runs Captive Portal services.
|
description: Here I'm going to write about my terrible time as an IT apprentice in two different small Norwegian companies. First one does web hosting, second runs Captive Portal services.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
To be quite frank I had a pretty terrible time as an apprentice.
|
||||||
|
With problems all the way.
|
||||||
|
It would've probably gone by fine if I didn't have any sleeping issues.
|
||||||
|
But that's whatever, cause still both companies I worked at as an apprentice in, in my opinion.
|
||||||
|
Should've never even been accepted to have apprentices in the first place.
|
||||||
|
As in Norway the company needs to be registered, with one guy who is supposed to teach stuff to the apprentices.
|
||||||
|
And another one whom which the apprentices may go to if any issue in the workplace should appear.
|
||||||
|
|
||||||
|
All of this was for sure very lacking in both companies.
|
||||||
|
And it feels like I was just a normal employee that got payed like :poop:.
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
In chronological order I got approved at Sircon kinda late, after we were done at school and most of the class already working.
|
||||||
|
And after finding two security issues and being late three times they fired me after only one and a half month.
|
||||||
|
Two months on paper, but I couldn't do any real IT work after they gave me a notice of the firing.
|
||||||
|
As they turned off my computer and removed my access to everything while I was given the talk.
|
||||||
|
|
||||||
|
After that I just barely got into some alternative thing at school.
|
||||||
|
That was kinda nice actually, and I probably should've just gone through with it.
|
||||||
|
Cause then I'd have my papers after only one year.
|
||||||
|
Whereas a normal apprenticeship in Norway is two years in almost all fields.
|
||||||
|
This alternative was also pretty fun.
|
||||||
|
Because my teacher let me work on whatever, as long as it was IT focused and I documented it.
|
||||||
|
|
||||||
|
The reason I still went to SkyLabs after all that is mostly money...
|
||||||
|
Which also happens to seem like the root of my biggest problems at that company...
|
||||||
|
12
content/blog/apprentice/expose/school.en.md
Normal file
12
content/blog/apprentice/expose/school.en.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-11T13:02:54Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['apprentice']
|
||||||
|
series: ['apprentice']
|
||||||
|
tags: ['apprentice', 'various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: School
|
||||||
|
description: My experience in some school apprenticeship alternative
|
||||||
|
---
|
@ -7,5 +7,7 @@ series: ['apprentice', 'expose']
|
|||||||
tags: ['apprentice', 'expose', 'various']
|
tags: ['apprentice', 'expose', 'various']
|
||||||
toc: true
|
toc: true
|
||||||
title: Sircon
|
title: Sircon
|
||||||
description:
|
description: My experience as an apprentice working for the hosting company Sircon
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
date: 2022-05-25T09:54:05Z
|
date: 2022-05-25T09:54:05Z
|
||||||
draft: true
|
draft: true
|
||||||
aliases: []
|
aliases: []
|
||||||
categories: ['various']
|
categories: ['apprentice', 'expose']
|
||||||
series: []
|
series: ['apprentice', 'expose']
|
||||||
tags: ['various']
|
tags: ['apprentice', 'expose', 'various']
|
||||||
toc: true
|
toc: true
|
||||||
title: Skylabs
|
title: Skylabs
|
||||||
description:
|
description: My experience as an apprentice working for the captive portal service company SkyLabs
|
||||||
---
|
---
|
13
content/blog/apprentice/flask.en.md
Normal file
13
content/blog/apprentice/flask.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:10:14Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Flask
|
||||||
|
description: Introduction to using Python Flask for creating web servers
|
||||||
|
---
|
||||||
|
|
@ -6,6 +6,7 @@ categories: ['various']
|
|||||||
series: []
|
series: []
|
||||||
tags: ['various']
|
tags: ['various']
|
||||||
toc: true
|
toc: true
|
||||||
title: Freeradius
|
title: FreeRADIUS
|
||||||
description:
|
description: FreeRADIUS basics and some bad production configs I've had to work with
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ docs:
|
|||||||
- url: https://handlebarsjs.com/guide/
|
- url: https://handlebarsjs.com/guide/
|
||||||
name: Handlebars official guide
|
name: Handlebars official guide
|
||||||
---
|
---
|
||||||
|
|
||||||
Handlebars.js is a templating engine like jinja2, but entirely in JavaScript.
|
Handlebars.js is a templating engine like jinja2, but entirely in JavaScript.
|
||||||
|
|
||||||
Personally I've only found pug templates very nice to work with as I love the minimal syntax.
|
Personally I've only found pug templates very nice to work with as I love the minimal syntax.
|
||||||
|
@ -7,6 +7,7 @@ series: []
|
|||||||
tags: ['various']
|
tags: ['various']
|
||||||
chroma: false
|
chroma: false
|
||||||
toc: true
|
toc: true
|
||||||
title: Jquery
|
title: jQuery
|
||||||
description:
|
description: The JavaScript library jQuery is often very useful for frontend web development, but it may easily lead to the dreaded "callback hell"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -7,5 +7,6 @@ series: []
|
|||||||
tags: ['various']
|
tags: ['various']
|
||||||
toc: true
|
toc: true
|
||||||
title: Meraki
|
title: Meraki
|
||||||
description:
|
description: Cisco Meraki basics and captive portal setup
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -8,5 +8,14 @@ tags: ['various']
|
|||||||
chroma: false
|
chroma: false
|
||||||
toc: true
|
toc: true
|
||||||
title: Mikrotik
|
title: Mikrotik
|
||||||
description:
|
description: Mikrotik routers has a lot of wierd stuff builtin, here is some of that
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
# Full disk problem
|
||||||
|
The built-in drive on the boxes may get filled up by hidden files...
|
||||||
|
There exists an official [fix_space](https://www.mikrotik.com/download/share/fix_space.npk) package, but finding that was a little adventure in itself reading through old forum threads from 2018...
|
||||||
|
|
||||||
|
|
||||||
|
# SkyLabs provisioning/setup problem
|
||||||
|
|
||||||
|
13
content/blog/apprentice/msal.en.md
Normal file
13
content/blog/apprentice/msal.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:10:19Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: MSAL
|
||||||
|
description: MSAL the new and cloud/Azure AD only version of the old ADAL, both of these are libraries that Microsoft has implemented in sevral programming languages for Active Directory authenitication built into whatever apps you're programming
|
||||||
|
---
|
||||||
|
|
13
content/blog/apprentice/postgres.en.md
Normal file
13
content/blog/apprentice/postgres.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:10:33Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: PostgreSQL
|
||||||
|
description: PostgreSQL is a pretty good SQL database server that even implements PL/pgSQL that is like a whole programming language on it's own
|
||||||
|
---
|
||||||
|
|
@ -39,31 +39,16 @@ And the fix for it was super easy!~
|
|||||||
|
|
||||||
We use a custom endpoint decorator, like flasks own @app.route, but we control what happens.
|
We use a custom endpoint decorator, like flasks own @app.route, but we control what happens.
|
||||||
It's here the authentication is implemented, on all requests that go to routes defined with this decorator will run the Access Control controller.
|
It's here the authentication is implemented, on all requests that go to routes defined with this decorator will run the Access Control controller.
|
||||||
Access Control goes through the auth options in it's dunder init function and sets self.current_user to some admin user from the database.
|
Access Control goes through the auth options in its dunder init function and sets self.current_user to some admin user from the database.
|
||||||
To fix the problem I could've made this check specific to the API keys by putting it in the API key controller that also has a user getter function.
|
To fix the problem I could've made this check specific to the API keys by putting it in the API key controller that also has a user getter function.
|
||||||
But I chose to add an if statement to the Access Control user getter, this is called by our endpoint decorator.
|
But I chose to add an if statement to the Access Control user getter, this is called by our endpoint decorator.
|
||||||
So I literally just changed this.
|
So I literally just changed this.
|
||||||
|
|
||||||
{{< highlight py >}}
|
{{< highlight py >}}{{% asset "apprentice/security/accesscontrol.py" %}}{{< /highlight >}}
|
||||||
class AccessControl:
|
|
||||||
current_user = None
|
|
||||||
...
|
|
||||||
def get_current_user(self):
|
|
||||||
return self.current_user
|
|
||||||
{{< /highlight >}}
|
|
||||||
|
|
||||||
To that!
|
To that!
|
||||||
|
|
||||||
{{< highlight py >}}
|
{{< highlight py >}}{{% asset "apprentice/security/accesscontrol-fix.py" %}}{{< /highlight >}}
|
||||||
class AccessControl:
|
|
||||||
current_user = None
|
|
||||||
...
|
|
||||||
def get_current_user(self):
|
|
||||||
if self.current_user.deleted is None:
|
|
||||||
return self.current_user
|
|
||||||
else:
|
|
||||||
raise Forbidden
|
|
||||||
{{< /highlight >}}
|
|
||||||
|
|
||||||
To fix this issue.
|
To fix this issue.
|
||||||
|
|
||||||
@ -74,6 +59,12 @@ The HTTP response will be error 500 if it's a Python error.
|
|||||||
But if it's one of our own error classes that's been raised.
|
But if it's one of our own error classes that's been raised.
|
||||||
Then an error code, description and status code will be taken from that exception class and sent as the response.
|
Then an error code, description and status code will be taken from that exception class and sent as the response.
|
||||||
|
|
||||||
|
There is also the case of the JWT secret key that's wayyy too small.
|
||||||
|
And not changed since it was introduced in the admin API.
|
||||||
|
Old keys is a theme on SkyLabs servers.
|
||||||
|
|
||||||
|
I'm surprised no one, as far as we know anyway, has broken into the servers yet.
|
||||||
|
|
||||||
#### Funny
|
#### Funny
|
||||||
Something I find kinda funny is the fact that those exception descriptions are in English, but the admin frontend is entirely in Norwegian only.
|
Something I find kinda funny is the fact that those exception descriptions are in English, but the admin frontend is entirely in Norwegian only.
|
||||||
It's even got a big ass JavaScript file that maps all the error codes to messages.
|
It's even got a big ass JavaScript file that maps all the error codes to messages.
|
||||||
@ -96,14 +87,27 @@ We use Ansible to manage the servers and deploy code updates.
|
|||||||
There is an Ansible var file that has a list of user accounts with options for shell and ssh pubkey.
|
There is an Ansible var file that has a list of user accounts with options for shell and ssh pubkey.
|
||||||
That file also has a list of old accounts that should be deleted.
|
That file also has a list of old accounts that should be deleted.
|
||||||
These are per employee users.
|
These are per employee users.
|
||||||
But the intrauser and ansible users have existed since 2016...
|
But the intrauser and Ansible users have existed since 2016...
|
||||||
using the same keys that they were created with.
|
using the same keys that they were created with.
|
||||||
The reason this is such a big threat is the fact that intrauser, ansible and all employee accounts has sudo access without password.
|
The reason this is such a big threat is the fact that the Ansible and all employee accounts has sudo access without password.
|
||||||
|
|
||||||
|
The intrauser does actually have a password set, but it may use sudo without password when running rsync.
|
||||||
|
And the thing is, [gtfobins](https://gtfobins.github.io/) has a privilege escalation exploit for rsync that our servers are vulnerable to.
|
||||||
|
|
||||||
So check it; these screenshots are from SkyLabs' Ansible git log!~
|
So check it; these screenshots are from SkyLabs' Ansible git log!~
|
||||||
|
|
||||||
{{< img src="apprentice/skyid/intrauser-key.png" caption="Me finally updating the key" >}}
|
{{< img src="apprentice/skyid/intrauser-key.png" caption="Me finally updating the key" >}}
|
||||||
|
|
||||||
|
And it's even worse... as it turns out the OpenVPN setup also has keys from guess when!
|
||||||
|
That's right! 2016...
|
||||||
|
So even if you only got access to the intrauser, it's super easy getting local privilege escalation on the servers.
|
||||||
|
|
||||||
|
To be honest I'm quite surprised the servers hasn't been pwnd big time!
|
||||||
|
|
||||||
|
As I've in fact proven that any ex-employee that has a copy of our Ansible repo could easily forge OpenVPN client keys and certificates.
|
||||||
|
And also got full root access over ssh anyway.
|
||||||
|
Just add a little [Tor](https://torproject.org/) magic on top of that, and you got full access to all the servers without us being able to trace it back...
|
||||||
|
|
||||||
## Sircon
|
## Sircon
|
||||||
These guys has a few beefy ass physical servers in their own rack in a supposedly EMP and fire safe room in the basement of their offices.
|
These guys has a few beefy ass physical servers in their own rack in a supposedly EMP and fire safe room in the basement of their offices.
|
||||||
Those physical machines run virtual machines that run WHM/cPanel and whatever PHP app the customer would like.
|
Those physical machines run virtual machines that run WHM/cPanel and whatever PHP app the customer would like.
|
||||||
@ -123,10 +127,7 @@ As they don't allow ssh access and PHP is configured to not allow anything dange
|
|||||||
|
|
||||||
Example .htaccess to run cgi scripts.
|
Example .htaccess to run cgi scripts.
|
||||||
|
|
||||||
{{< highlight apache >}}
|
{{< highlight apache >}}{{% asset "apprentice/security/htaccess" %}}{{< /highlight >}}
|
||||||
Options +ExecCGI
|
|
||||||
AddHandler cgi-script .cgi .pl .py .sh .rb
|
|
||||||
{{< /highlight >}}
|
|
||||||
|
|
||||||
The fix for this is the [AllowOverride](https://httpd.apache.org/docs/current/mod/core.html#allowoverride) directive.
|
The fix for this is the [AllowOverride](https://httpd.apache.org/docs/current/mod/core.html#allowoverride) directive.
|
||||||
Just make sure it does not include "FileInfo".
|
Just make sure it does not include "FileInfo".
|
||||||
|
@ -7,7 +7,7 @@ series: ['apprentice']
|
|||||||
tags: ['python']
|
tags: ['python']
|
||||||
toc: true
|
toc: true
|
||||||
title: SQLAlchemy
|
title: SQLAlchemy
|
||||||
description:
|
description: SQLAlchemy is a so called "Object Relational Mapper" for Python, these ORMs are very useful as you can get IDE support for interacting with the database
|
||||||
---
|
---
|
||||||
|
|
||||||
[SQLAlchemy docs](https://docs.sqlalchemy.org/en/latest/orm/quickstart.html)
|
[SQLAlchemy docs](https://docs.sqlalchemy.org/en/latest/orm/quickstart.html)
|
||||||
|
13
content/blog/apprentice/whm.en.md
Normal file
13
content/blog/apprentice/whm.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:07:06Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: WHM
|
||||||
|
description: Web Host Manager, this is the secret sauce for many companies that do shared Linux server web hosting with cPanel
|
||||||
|
---
|
||||||
|
|
13
content/blog/apprentice/wordpress.en.md
Normal file
13
content/blog/apprentice/wordpress.en.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-20T14:06:54Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: WordPress
|
||||||
|
description: The worlds most popular and whackiest web page Content Management System
|
||||||
|
---
|
||||||
|
|
59
content/cv.en.md
Normal file
59
content/cv.en.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-19T15:24:00Z
|
||||||
|
draft: true
|
||||||
|
aliases: ['Curriculum Vitae']
|
||||||
|
categories: ['meta']
|
||||||
|
series: ['me']
|
||||||
|
tags: ['meta']
|
||||||
|
toc: true
|
||||||
|
title: My CV
|
||||||
|
description: My CV/Curriculum Vitae
|
||||||
|
---
|
||||||
|
|
||||||
|
{{< img src="me.jpg" caption="Me, Sivert V. Sæther" >}}
|
||||||
|
|
||||||
|
Hi, I'm Sivert V. Sæther.
|
||||||
|
|
||||||
|
I do computer stuff, like programming and Linux server administration.
|
||||||
|
|
||||||
|
## Experience
|
||||||
|
### IT apprentice
|
||||||
|
#### SkyLabs AS
|
||||||
|
##### 1. May 2021 - 31. October 2022
|
||||||
|
SkyLabs provide captive portal services with lots of supported authentication methods.
|
||||||
|
Here I did lots of programming, administered the servers and even did production code deployments!
|
||||||
|
|
||||||
|
|
||||||
|
#### Sircon Norge AS
|
||||||
|
##### 21. September 2020 - 19. November 2020
|
||||||
|
Technical cPanel/WHM support with customers mainly running WordPress.
|
||||||
|
|
||||||
|
### Summer jobs
|
||||||
|
#### Housing Association Janitor
|
||||||
|
##### Juli 2017 - August 2017, and Juni 2019 - Juli 2019
|
||||||
|
This was a very good program with good pay where we did various tasks in the area where we lived.
|
||||||
|
Such as cutting the grass, trimming bushes and painting stuff.
|
||||||
|
|
||||||
|
## Education
|
||||||
|
### Norwegian high school
|
||||||
|
Heimdal videregående skole 2019 - 2020 /
|
||||||
|
VG2 IKT-Servicefag (IT program with focus on support)
|
||||||
|
|
||||||
|
Byåsen videregående skole 2018 - 2019 /
|
||||||
|
VG1 Studiespesialisering (higher education prep program)
|
||||||
|
|
||||||
|
## Hobbies
|
||||||
|
I operate several hobby Linux servers, and program cool stuff.
|
||||||
|
I'm a big Docker fan, running most of the services on those servers in it.
|
||||||
|
As for programming I'd say I'm definitely most fluent in Python, but I'm a big fan of rust and go.
|
||||||
|
I also like gaming and parties.
|
||||||
|
|
||||||
|
## References
|
||||||
|
Name - Workplace - phone number
|
||||||
|
|
||||||
|
{{< block >}}
|
||||||
|
Jørgen Kirsebom - IT Luftkrigsskolen (IT Department The Air War School in Trondheim) - [+47 951 49 735](tel:+4795149735)
|
||||||
|
Knut Eivind Brennhaug - Main teacher IKT-servicefag/IT - [+47 975 15 183](tel:+4797515183)
|
||||||
|
Kenneth Bjørsvik - SkyLabs AS - [+47 959 37 212](tel:+4795937212)
|
||||||
|
Nils R. Grotnes - SkyLabs AS - [+47 904 25 862](tel:+4790425862)
|
||||||
|
{{< /block >}}
|
21
content/cv.no.md
Normal file
21
content/cv.no.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-19T15:24:03Z
|
||||||
|
draft: true
|
||||||
|
aliases: ['Curriculum Vitae']
|
||||||
|
categories: ['meta']
|
||||||
|
series: ['me']
|
||||||
|
tags: ['meta']
|
||||||
|
toc: true
|
||||||
|
title: Min CV
|
||||||
|
description: Min CV/Curriculum Vitae
|
||||||
|
tocExtra: |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Referanser
|
||||||
|
|
||||||
|
Navn – Arbeidsplass – telefon nummer
|
||||||
|
|
||||||
|
Jørgen Kirsebom - IT Luftkrigsskolen - +47 951 49 735
|
||||||
|
Knut Eivind Brennhaug - Kontaktlærer fra IKT - +47 975 15 183
|
||||||
|
Kenneth Bjørsvik - SkyLabs AS - +47 959 37 212
|
@ -5,6 +5,6 @@
|
|||||||
{{ $content := .Inner }}
|
{{ $content := .Inner }}
|
||||||
{{ $in := split .Inner "\n" }}
|
{{ $in := split .Inner "\n" }}
|
||||||
{{ if gt (len $in) 1 }}
|
{{ if gt (len $in) 1 }}
|
||||||
{{ $content = (delimit (after 1 $in) "\n") }}
|
{{ $content = (delimit $in "\n") }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ transform.Highlight (htmlUnescape $content) (.Get 0) (delimit $opts ",") }}
|
{{ transform.Highlight (htmlUnescape $content) (.Get 0) (delimit $opts ",") }}
|
9
layouts/shortcodes/video.html
Normal file
9
layouts/shortcodes/video.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<video
|
||||||
|
width='{{ with.Get 2 }}{{ . }}{{ else }}100%{{ end }}'
|
||||||
|
height='{{ with .Get 3 }}{{ . }}{{ else }}100%{{ end }}'
|
||||||
|
{{ with .Get 1 }}{{ . }}{{ else }}controls{{ end }}>
|
||||||
|
{{ with resources.Get (.Get 0) }}
|
||||||
|
<source src='{{ .RelPermalink }}' type='{{ .MediaType }}'>
|
||||||
|
{{ end }}
|
||||||
|
<b>Your browser does not support the video tag!</b>
|
||||||
|
</video>
|
Loading…
Reference in New Issue
Block a user